Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ Progressive learning, adaptive memory, and research expansion.
- `hooks/SecurityValidator.hook.ts`, `hooks/SecretScanner.hook.ts` — security prefix rebranded to `[KAI SECURITY]`
- `hooks/lib/change-detection.ts` — system label rebranded to `KAI System`
- `PAI/Tools/RoutingAudit.ts` — MEM_PREFIX derived from HOME (portable, no hardcoded paths)
- Manifest updated: <!-- KAI:counts:skills:begin -->70<!-- KAI:counts:skills:end --> skills, <!-- KAI:counts:hooks:begin -->59<!-- KAI:counts:hooks:end --> hooks, <!-- KAI:counts:agents:begin -->20<!-- KAI:counts:agents:end --> agents
- Manifest updated: <!-- KAI:counts:skills:begin -->71<!-- KAI:counts:skills:end --> skills, <!-- KAI:counts:hooks:begin -->61<!-- KAI:counts:hooks:end --> hooks, <!-- KAI:counts:agents:begin -->20<!-- KAI:counts:agents:end --> agents
- VERSION 5.2.0 → 5.6.0
- README, QUICKSTART, WHATS-DIFFERENT, releases/README updated to KAI 5.6.0

Expand Down Expand Up @@ -354,7 +354,7 @@ Initial public release of KAI (Kaizen AI).

### Features
- **Algorithm v3.13.0** — Parallelization gate, phantom capability prune, version centralization
- **<!-- KAI:counts:skills:begin -->70<!-- KAI:counts:skills:end --> skills** — Research, Security, Analysis, Writing, Engineering Manager workflows, and more
- **<!-- KAI:counts:skills:begin -->71<!-- KAI:counts:skills:end --> skills** — Research, Security, Analysis, Writing, Engineering Manager workflows, and more
- **47 hooks** — Lifecycle automation including SecretScanner, GitHubWriteGuard, RatingCapture, BuildSettings
- **18 named agents** — Architect, Engineer, researchers, Pentester, and domain specialists
- **Memory system** — Cross-project knowledge distillation, staging, curation
Expand Down
2 changes: 1 addition & 1 deletion MEMORY/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# MEMORY - Unified Memory System

**Version:** 7.4.2 (Projects-native architecture, 2026-01-12)
**Version:** 7.7.0 (Projects-native architecture, 2026-01-12)

Full documentation: `~/.claude/skills/PAI/MEMORYSYSTEM.md`

Expand Down
2 changes: 1 addition & 1 deletion PAI/ACTIONS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Actions

> **KAI 7.4.2** — Stable release.
> **KAI 7.7.0** — Stable release.

**Atomic, Composable Units of Work**

Expand Down
2 changes: 1 addition & 1 deletion PAI/ACTIONS/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PAI Actions

> **KAI 7.4.2** — Stable release.
> **KAI 7.7.0** — Stable release.

Atomic, composable units of work. Each action does one thing, takes JSON in, returns JSON out.

Expand Down
2 changes: 1 addition & 1 deletion PAI/AISTEERINGRULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ Personal overrides in `USER/AISTEERINGRULES.md`. Full examples in `AISTEERINGRUL

**Memcarry capture — offer to capture a durable lesson when {PRINCIPAL.NAME} establishes one.** The forward half of the cross-project cycle (capture; backflow above is the B→A half). When {PRINCIPAL.NAME} states a durable, reusable rule during the work ("remember this", "from now on…", "the lesson here is…", or clearly articulates a cross-project gotcha), OFFER to capture it as a lesson atom: draft the `WHEN → DO → BECAUSE` (the `because` should carry a dated evidence anchor) and preview it via `memcarry capture-lesson --when "…" --do "…" --because "…" --trigger "a,b" [--scope global|project:<name>]` (no `--apply`, writes nothing — it also dup-checks and reports any `similar` existing lesson or exact-id `collision`). If it surfaces a `similar`/`collision` hit, prefer `refine` of that atom over a new one. Default `--scope global` when the rule is cross-project; `project:<name>` when it's specific to one project. Only AFTER explicit confirmation, add `--apply`. NEVER pass `--apply` without confirmation — same anti-circular-loop gate as backflow (a lesson gains `human-confirmed` authority from {PRINCIPAL.NAME}, never my own assertion). Don't auto-capture, don't pump the store with marginal lessons — capture only what {PRINCIPAL.NAME} confirms is worth keeping. CLI/store paths same as backflow above.

**Checkpoint after a subagent returns (capture-loss guard).** Subagents (the Agent/Task tool) CANNOT persist memory themselves — no CLAUDE.md, no memory hooks fire inside them. The parent is the ONLY actor that can save what a subagent learned; if the parent doesn't, the learning is lost (this is the rayhunter loss class amplified). When a Task subagent returns having established something durable — a cross-project lesson, a project fact, a resolved gotcha, a verified state — checkpoint it before moving on: a `memcarry capture-lesson` (per the rule above) for a reusable lesson, or a project-memory note for a project fact. The `AgentMemoryCapture` hook surfaces a `<system-reminder>` on substantive returns as a prompt — act on it when the subagent's work is worth keeping, ignore it when nothing durable was learned. Don't capture marginal subagent output; do capture what you'd be annoyed to re-derive next session.
**Checkpoint after an Agent subagent returns (capture-loss guard).** Subagents cannot persist memory themselves — no parent memory hooks fire inside them. The parent is the ONLY actor that can save what a subagent learned; if the parent doesn't, the learning is lost (this is the rayhunter loss class amplified). When an Agent subagent returns having established something durable — a cross-project lesson, a project fact, a resolved gotcha, a verified state, or a `Durable findings for parent checkpoint:` section — checkpoint it before moving on: a `memcarry capture-lesson` (per the rule above) for a reusable lesson, or a project-memory note for a project fact. The `AgentMemoryCapture` hook surfaces a `<system-reminder>` on substantive or explicitly marked durable returns as a prompt — act on it when the subagent's work is worth keeping, ignore it when nothing durable was learned. Don't capture marginal subagent output; do capture what you'd be annoyed to re-derive next session.
2 changes: 1 addition & 1 deletion PAI/CLI.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
> **KAI 7.4.2** — Stable release.
> **KAI 7.7.0** — Stable release.

# PAI Command-Line Tools

Expand Down
2 changes: 1 addition & 1 deletion PAI/FLOWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Flows

> **KAI 7.4.2** — Stable release.
> **KAI 7.7.0** — Stable release.

**Connecting Sources to Pipelines on a Schedule**

Expand Down
2 changes: 1 addition & 1 deletion PAI/FLOWS/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PAI Flows

> **KAI 7.4.2** — Stable release.
> **KAI 7.7.0** — Stable release.

Flows connect **sources** to **pipelines** on a **schedule**. A flow fetches content from an external source (RSS feed, API, etc.), pipes it through a pipeline of actions, and delivers results to a destination (email, webhook, etc.).

Expand Down
2 changes: 1 addition & 1 deletion PAI/PIPELINES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Pipelines

> **KAI 7.4.2** — Stable release.
> **KAI 7.7.0** — Stable release.

**Chaining Actions into Sequential Workflows**

Expand Down
2 changes: 1 addition & 1 deletion PAI/PIPELINES/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PAI Pipelines

> **KAI 7.4.2** — Stable release.
> **KAI 7.7.0** — Stable release.

Pipelines chain actions together. A pipeline is just a list of actions executed in order using the **pipe model** — the output of each action becomes the input of the next.

Expand Down
20 changes: 20 additions & 0 deletions PAI/THEDELEGATIONSYSTEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,26 @@ claude --attach <session-id>
- ❌ No callback/RPC from background to foreground (use file-based coordination)
- ❌ Don't rely on background agents to report back synchronously (they may outlive parent)

### Context Envelope for Background / SDK Sessions

Background and SDK-launched sessions do not share the parent conversation. Prepend the same narrow
PAI handoff used for context-sensitive `Explore` / `Plan` delegation:

```text
<pai-agent-context-handoff tier="rules">
- Follow current repo rules before acting: branch-only workflow, read before modifying, minimal scope, verify before reporting done.
- Do not mutate public KAI, remotes, PRs, or release artifacts unless the parent prompt includes explicit current-turn approval.
- Preserve user and memory content. Never delete, move, or rewrite memory based on format judgment.
- Subagents cannot persist durable memory. If you learn something the parent should save, return a section named "Durable findings for parent checkpoint:" with only the facts worth keeping.
- ADA pointer: if the task touches ambient domain activation or delegation reach, read docs/planning/ambient-domain-activation-design.md and docs/planning/ROADMAP-7.x.md before changing behavior.
</pai-agent-context-handoff>
<pai-background-delegation-boundary>
- Background/SDK sessions do not share parent conversation state. Include only task-relevant paths, constraints, approvals, and this handoff block.
- Do not include private conversation history, unrelated memories, credentials, tokens, or public-KAI-prohibited content.
- Coordinate through commits, PR comments, or explicit task/status files; do not assume synchronous return to the parent session.
</pai-background-delegation-boundary>
```

### Model Selection for Background Sessions

Background sessions have different cost/capability tradeoffs than intra-session agents:
Expand Down
2 changes: 1 addition & 1 deletion PAI/Tools/Banner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ interface Stats {

function getStats(): Stats {
let name = "PAI";
let paiVersion = "7.4.2";
let paiVersion = "7.7.0";
let algorithmVersion = "3.14.0";
let skills = 0, workflows = 0, hooks = 0, learnings = 0, userFiles = 0;
let connection = "API";
Expand Down
2 changes: 1 addition & 1 deletion PAI/Tools/BuildCLAUDE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function loadVariables(): Record<string, string> {
"{DAIDENTITY.DISPLAYNAME}": settings.daidentity?.displayName || "Assistant",
"{PRINCIPAL.NAME}": settings.principal?.name || "User",
"{PRINCIPAL.TIMEZONE}": settings.principal?.timezone || "UTC",
"{{PAI_VERSION}}": settings.pai?.version || "7.4.2",
"{{PAI_VERSION}}": settings.pai?.version || "7.7.0",
"{{PRODUCT_NAME}}": settings.pai?.productName || "PAI",
"{{ALGO_VERSION}}": algoVersion,
"{{ALGO_PATH}}": `PAI/Algorithm/${algoVersion}.md`,
Expand Down
152 changes: 126 additions & 26 deletions PAI/Tools/MemoryCurate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ function approveDraft(index: number): { success: boolean; error?: string } {
const targetPath = join(targetMemoryDir, draft.targetFilename);

try {
// Write the draft content (without frontmatter) to target
// Write new targets with frontmatter; existing targets are append-only so repeated fixed-name
// feedback drafts cannot clobber earlier approved memory or hand edits.
const memContent = `---
type: ${draft.type.includes('success') ? 'feedback' : 'project'}
description: ${draft.title}
Expand All @@ -333,7 +334,14 @@ source_session: ${draft.sourceSession}

${draft.content}
`;
writeFileSync(targetPath, memContent);
if (!existsSync(targetPath)) {
writeFileSync(targetPath, memContent);
} else {
const existing = readFileSync(targetPath, 'utf-8');
const approvedDate = new Date().toISOString().split('T')[0];
const entry = `\n## ${draft.title}\n_source: auto-generated (approved ${approvedDate}) · source_session: ${draft.sourceSession}_\n\n${draft.content.trim()}\n`;
writeFileSync(targetPath, existing.trimEnd() + '\n' + entry);
}

// Update MEMORY.md index
const memoryMd = join(targetMemoryDir, 'MEMORY.md');
Expand Down Expand Up @@ -409,6 +417,20 @@ function parseInsight(content: string): { title: string; category: string; confi
};
}

function listCandidateInsightFiles(): string[] {
if (!existsSync(INSIGHTS_DIR)) return [];
return readdirSync(INSIGHTS_DIR)
.filter(f => f.endsWith('.md'))
.filter(f => {
try {
return readFileSync(join(INSIGHTS_DIR, f), 'utf-8').includes('status: candidate');
} catch {
return false;
}
})
.sort();
}

/**
* Promote an INSIGHTS candidate into a consolidated per-project memory file.
* Appends the lesson under a dated bullet to `insights_promoted.md`, updates the
Expand Down Expand Up @@ -463,6 +485,50 @@ function promoteInsight(filename: string, projectOverride?: string): { success:
}
}

interface InsightPromotionRequest {
filename: string;
project?: string;
}

interface InsightPromotionBatchResult {
promoted: number;
failed: { filename: string; error: string }[];
targets: string[];
}

function parseInsightPromotionManifest(manifestPath: string): InsightPromotionRequest[] {
const lines = readFileSync(manifestPath, 'utf-8')
.split('\n')
.map(line => line.trim())
.filter(line => line && !line.startsWith('#'));

return lines.map(line => {
const [filename, project] = line.split('\t').map(part => part.trim());
if (!filename) throw new Error(`Invalid promotion manifest row: ${line}`);
return { filename, project: project || undefined };
});
}

function promoteInsightBatch(requests: InsightPromotionRequest[], projectOverride?: string): InsightPromotionBatchResult {
const result: InsightPromotionBatchResult = { promoted: 0, failed: [], targets: [] };
for (const request of requests) {
const promoted = promoteInsight(request.filename, projectOverride || request.project);
if (promoted.success) {
result.promoted += 1;
if (promoted.target && !result.targets.includes(promoted.target)) result.targets.push(promoted.target);
} else {
result.failed.push({ filename: request.filename, error: promoted.error || 'unknown error' });
}
}
return result;
}

function printBatchPromotionResult(result: InsightPromotionBatchResult): void {
if (result.promoted > 0) console.log(green(` ✓ Promoted ${result.promoted} insight(s).`));
for (const target of result.targets) console.log(dim(` → ${target}`));
for (const failure of result.failed) console.log(red(` ✗ ${failure.filename}: ${failure.error}`));
}

// ============================================================================
// Interactive single-key prompt
// ============================================================================
Expand Down Expand Up @@ -663,31 +729,33 @@ async function interactiveInsights(dryRun: boolean): Promise<void> {
console.log(dim(' Run `pai curate drafts` after those sessions to review generated memories.'));
}

// Show insight candidates from InsightExtractor
const insightsDir = join(paiDir, 'MEMORY', 'LEARNING', 'INSIGHTS');
if (existsSync(insightsDir)) {
const insightFiles = readdirSync(insightsDir).filter(f => f.endsWith('.md'));
const candidates = insightFiles.filter(f => {
const candidates = listCandidateInsightFiles();
if (candidates.length > 0) {
console.log(`\n ${bold(String(candidates.length))} insight candidate(s) pending review:`);
for (const f of dryRun ? candidates.slice(0, 5) : candidates) {
try {
const content = readFileSync(join(insightsDir, f), 'utf-8');
return content.includes('status: candidate');
} catch { return false; }
});
if (candidates.length > 0) {
console.log(`\n ${bold(String(candidates.length))} insight candidate(s) pending review:`);
for (const f of candidates.slice(0, 5)) {
try {
const content = readFileSync(join(insightsDir, f), 'utf-8');
const titleMatch = content.match(/title:\s*"([^"]+)"/);
const title = titleMatch ? titleMatch[1] : f;
console.log(` • ${title}`);
} catch { console.log(` • ${f}`); }
}
if (candidates.length > 5) {
console.log(dim(` ... and ${candidates.length - 5} more`));
const content = readFileSync(join(INSIGHTS_DIR, f), 'utf-8');
const insight = parseInsight(content);
console.log(` • ${insight.title || f}`);
} catch { console.log(` • ${f}`); }

if (!dryRun) {
const key = await prompt(
` Action: ${green('[p]')}romote to kai ${dim('[s]')}kip ${dim('[q]')}uit → `,
['p', 's', 'q']
);
if (key === 'q') { console.log(dim(' (quit)\n')); return; }
if (key === 'p') {
const result = promoteInsight(f);
if (result.success) console.log(green(` ✓ Promoted → ${result.target}`));
else console.log(red(` ✗ Failed: ${result.error}`));
}
}
console.log(dim(' Promote to project memory with `pai curate promote <filename>`'));
}
if (dryRun && candidates.length > 5) {
console.log(dim(` ... and ${candidates.length - 5} more`));
}
console.log(dim(' Batch promote with `pai curate promote --from-manifest <file>` or `pai curate promote --all <project>`.'));
}

console.log();
Expand Down Expand Up @@ -913,6 +981,8 @@ if (flags.includes('--help') || subcommand === 'help') {
${cyan('pai curate reject <n>')} Reject draft #n
${cyan('pai curate restore <proj> <f>')} Restore archived file
${cyan('pai curate promote <f> [--project <p>]')} Promote an INSIGHTS candidate to project memory
${cyan('pai curate promote --from-manifest <file>')} Batch promote <file><TAB><project> rows
${cyan('pai curate promote --all <project>')} Promote every candidate insight to one project
${cyan('pai curate check')} Validate knowledge + detect contradictions
${cyan('pai curate approve-all')} Auto-approve eligible drafts (≥14d, conf≥0.8)
${cyan('pai curate approve-all --dry-run')} Preview what would be promoted
Expand Down Expand Up @@ -972,13 +1042,43 @@ switch (subcommand) {
break;
}
case 'promote': {
const projIdx = process.argv.indexOf('--project');
const projectOverride = projIdx !== -1 ? process.argv[projIdx + 1] : undefined;
if (flags.includes('--from-manifest')) {
const manifestPath = subArg;
if (!manifestPath) {
console.log(red(' Usage: pai curate promote --from-manifest <manifest.tsv> [--project <name>]'));
process.exit(1);
}
try {
const requests = parseInsightPromotionManifest(manifestPath);
const result = promoteInsightBatch(requests, projectOverride);
printBatchPromotionResult(result);
if (result.failed.length > 0) process.exit(1);
} catch (e: unknown) {
console.log(red(` ✗ ${e}`));
process.exit(1);
}
break;
}
if (flags.includes('--all')) {
const project = projectOverride || subArg;
if (!project) {
console.log(red(' Usage: pai curate promote --all <project>'));
process.exit(1);
}
const requests = listCandidateInsightFiles().map(filename => ({ filename, project }));
const result = promoteInsightBatch(requests);
printBatchPromotionResult(result);
if (result.failed.length > 0) process.exit(1);
break;
}

const filename = subArg;
if (!filename) {
console.log(red(' Usage: pai curate promote <insight-filename> [--project <name>]'));
process.exit(1);
}
const projIdx = process.argv.indexOf('--project');
const projectOverride = projIdx !== -1 ? process.argv[projIdx + 1] : undefined;
const result = promoteInsight(filename, projectOverride);
if (result.success) {
console.log(green(` ✓ Promoted ${basename(filename)} → ${result.target}`));
Expand Down
Loading
Loading