brainclaw 0.28.0 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +193 -170
- package/dist/brainclaw-vscode.vsix +0 -0
- package/dist/cli.js +683 -23
- package/dist/commands/accept.js +3 -0
- package/dist/commands/add-step.js +11 -26
- package/dist/commands/agent-board.js +70 -3
- package/dist/commands/audit.js +19 -0
- package/dist/commands/check-policy.js +54 -0
- package/dist/commands/check-security-mcp.js +145 -0
- package/dist/commands/check-security.js +106 -0
- package/dist/commands/claim-resource.js +1 -0
- package/dist/commands/codev.js +672 -0
- package/dist/commands/compact.js +74 -0
- package/dist/commands/complete-step.js +16 -26
- package/dist/commands/constraint.js +8 -20
- package/dist/commands/decision.js +9 -20
- package/dist/commands/delete-plan.js +10 -12
- package/dist/commands/delete-step.js +16 -0
- package/dist/commands/dispatch.js +163 -0
- package/dist/commands/doctor.js +1122 -49
- package/dist/commands/enable-agent.js +1 -0
- package/dist/commands/export.js +280 -22
- package/dist/commands/handoff.js +33 -0
- package/dist/commands/harvest.js +189 -0
- package/dist/commands/hooks.js +82 -25
- package/dist/commands/inbox.js +169 -0
- package/dist/commands/init.js +38 -31
- package/dist/commands/install-hooks.js +71 -44
- package/dist/commands/link.js +89 -0
- package/dist/commands/list-claims.js +48 -3
- package/dist/commands/list-plans.js +129 -25
- package/dist/commands/loops-handlers.js +409 -0
- package/dist/commands/mcp-read-handlers.js +1628 -0
- package/dist/commands/mcp-schemas.generated.js +74 -0
- package/dist/commands/mcp.js +4244 -1475
- package/dist/commands/plan-resource.js +64 -0
- package/dist/commands/plan.js +12 -26
- package/dist/commands/prune.js +37 -2
- package/dist/commands/reflect.js +20 -7
- package/dist/commands/release-claim.js +11 -6
- package/dist/commands/release-notes.js +170 -0
- package/dist/commands/repair.js +210 -0
- package/dist/commands/run-profile.js +57 -0
- package/dist/commands/sequence.js +113 -0
- package/dist/commands/session-end.js +423 -14
- package/dist/commands/session-start.js +214 -41
- package/dist/commands/setup-security.js +103 -0
- package/dist/commands/setup.js +42 -4
- package/dist/commands/stale.js +109 -0
- package/dist/commands/switch.js +131 -10
- package/dist/commands/trap.js +14 -31
- package/dist/commands/update-handoff.js +63 -4
- package/dist/commands/update-plan.js +21 -28
- package/dist/commands/update-step.js +37 -0
- package/dist/commands/upgrade.js +313 -6
- package/dist/commands/usage.js +102 -0
- package/dist/commands/version.js +20 -0
- package/dist/commands/who.js +124 -0
- package/dist/commands/worktree.js +105 -0
- package/dist/core/actions.js +315 -0
- package/dist/core/agent-capability.js +610 -17
- package/dist/core/agent-context.js +7 -1
- package/dist/core/agent-files.js +1169 -85
- package/dist/core/agent-integrations.js +160 -5
- package/dist/core/agent-inventory.js +2 -0
- package/dist/core/agent-profiles.js +93 -0
- package/dist/core/agent-registry.js +162 -30
- package/dist/core/agentrun-reconciler.js +345 -0
- package/dist/core/agentruns.js +424 -0
- package/dist/core/ai-agent-detection.js +31 -10
- package/dist/core/archival.js +77 -0
- package/dist/core/assignment-sweeper.js +82 -0
- package/dist/core/assignments.js +367 -0
- package/dist/core/audit.js +30 -0
- package/dist/core/bootstrap.js +61 -10
- package/dist/core/brainclaw-version.js +94 -2
- package/dist/core/candidates.js +93 -2
- package/dist/core/claims.js +419 -0
- package/dist/core/codev-metrics.js +77 -0
- package/dist/core/codev-personas.js +31 -0
- package/dist/core/codev-plan-gen.js +35 -0
- package/dist/core/codev-prompts.js +74 -0
- package/dist/core/codev-responses.js +62 -0
- package/dist/core/codev-rounds.js +218 -0
- package/dist/core/config.js +4 -0
- package/dist/core/context.js +454 -34
- package/dist/core/coordination.js +201 -6
- package/dist/core/cross-project.js +230 -16
- package/dist/core/default-profiles/doctor.yaml +11 -0
- package/dist/core/default-profiles/janitor.yaml +11 -0
- package/dist/core/default-profiles/onboarder.yaml +11 -0
- package/dist/core/default-profiles/reviewer.yaml +13 -0
- package/dist/core/dispatcher.js +1189 -0
- package/dist/core/duplicates.js +2 -2
- package/dist/core/entity-operations.js +450 -0
- package/dist/core/entity-registry.js +344 -0
- package/dist/core/event-log.js +1 -0
- package/dist/core/events.js +106 -2
- package/dist/core/execution-adapters.js +154 -0
- package/dist/core/execution-context.js +63 -0
- package/dist/core/execution-profile.js +270 -0
- package/dist/core/execution.js +255 -0
- package/dist/core/facade-schema.js +81 -0
- package/dist/core/federation-cloud.js +99 -0
- package/dist/core/federation-message.js +52 -0
- package/dist/core/federation-transport.js +65 -0
- package/dist/core/gc-semantic.js +482 -0
- package/dist/core/governance.js +247 -0
- package/dist/core/guards.js +19 -0
- package/dist/core/ideation.js +72 -0
- package/dist/core/identity.js +252 -28
- package/dist/core/ids.js +6 -0
- package/dist/core/input-validation.js +2 -2
- package/dist/core/instruction-templates.js +344 -136
- package/dist/core/io.js +90 -11
- package/dist/core/lock.js +6 -2
- package/dist/core/loops/brief-assembly.js +213 -0
- package/dist/core/loops/facade-schema.js +148 -0
- package/dist/core/loops/index.js +7 -0
- package/dist/core/loops/iteration-engine.js +139 -0
- package/dist/core/loops/lock.js +385 -0
- package/dist/core/loops/store.js +201 -0
- package/dist/core/loops/types.js +403 -0
- package/dist/core/loops/verbs.js +534 -0
- package/dist/core/markdown.js +15 -3
- package/dist/core/memory-compactor.js +432 -0
- package/dist/core/memory-git.js +152 -8
- package/dist/core/messaging.js +278 -0
- package/dist/core/migration.js +32 -1
- package/dist/core/mutation-pipeline.js +4 -2
- package/dist/core/operations/memory-mutation.js +129 -0
- package/dist/core/operations/memory-write.js +78 -0
- package/dist/core/operations/plan.js +190 -0
- package/dist/core/policy.js +169 -0
- package/dist/core/repo-analysis.js +67 -0
- package/dist/core/reputation.js +9 -3
- package/dist/core/schema.js +546 -21
- package/dist/core/search.js +21 -2
- package/dist/core/security-cache.js +71 -0
- package/dist/core/security-guard.js +152 -0
- package/dist/core/security-scoring.js +86 -0
- package/dist/core/sequence.js +130 -0
- package/dist/core/socket-client.js +113 -0
- package/dist/core/staleness.js +246 -0
- package/dist/core/state.js +98 -22
- package/dist/core/store-resolution.js +54 -12
- package/dist/core/toml-writer.js +76 -0
- package/dist/core/upgrades/backup.js +232 -0
- package/dist/core/upgrades/health-check.js +169 -0
- package/dist/core/upgrades/patches/candidate-archive.js +145 -0
- package/dist/core/upgrades/patches/handoff-review-strip.js +128 -0
- package/dist/core/upgrades/patches/provenance-rollout.js +136 -0
- package/dist/core/upgrades/schema-version.js +97 -0
- package/dist/core/worktree.js +606 -0
- package/dist/facts.js +114 -0
- package/dist/facts.json +111 -0
- package/docs/architecture/project-refs.md +5 -1
- package/docs/cli.md +690 -43
- package/docs/concepts/ideation-loop.md +317 -0
- package/docs/concepts/loop-engine.md +456 -0
- package/docs/concepts/mcp-governance.md +268 -0
- package/docs/concepts/memory-staleness.md +122 -0
- package/docs/concepts/multi-agent-workflows.md +166 -0
- package/docs/concepts/plans-and-claims.md +31 -6
- package/docs/concepts/project-md-convention.md +35 -0
- package/docs/concepts/troubleshooting.md +220 -0
- package/docs/concepts/upgrade-cli.md +202 -0
- package/docs/concepts/upgrade-dogfood-procedure.md +114 -0
- package/docs/context-format-changelog.md +2 -2
- package/docs/context-format.md +2 -2
- package/docs/index.md +68 -0
- package/docs/integrations/agents.md +15 -16
- package/docs/integrations/cline.md +88 -0
- package/docs/integrations/codex.md +75 -23
- package/docs/integrations/continue.md +60 -0
- package/docs/integrations/copilot.md +67 -9
- package/docs/integrations/kilocode.md +72 -0
- package/docs/integrations/mcp.md +304 -21
- package/docs/integrations/mistral-vibe.md +122 -0
- package/docs/integrations/opencode.md +84 -0
- package/docs/integrations/overview.md +23 -8
- package/docs/integrations/roo.md +74 -0
- package/docs/integrations/windsurf.md +83 -0
- package/docs/mcp-schema-changelog.md +191 -1
- package/docs/playbooks/integration/index.md +121 -0
- package/docs/playbooks/productivity/index.md +102 -0
- package/docs/playbooks/team/index.md +122 -0
- package/docs/product/agent-first-model.md +184 -0
- package/docs/product/entity-model-audit.md +462 -0
- package/docs/quickstart-existing-project.md +135 -0
- package/docs/quickstart.md +124 -37
- package/docs/release-maintenance.md +79 -0
- package/docs/review.md +2 -0
- package/docs/server-operations.md +118 -0
- package/package.json +20 -12
- package/dist/commands/claude-desktop-extension.js +0 -18
- package/dist/commands/diff.js +0 -99
- package/dist/core/claude-desktop-extension.js +0 -224
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { createWorktree, listWorktrees, removeWorktree, pruneWorktrees, cleanMergedWorktrees, mergeWorktreeBranch, worktreesBaseDir, } from '../core/worktree.js';
|
|
2
|
+
import { memoryExists } from '../core/io.js';
|
|
3
|
+
import { resolveTargetStore } from '../core/store-resolution.js';
|
|
4
|
+
export function runWorktreeCreate(options) {
|
|
5
|
+
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
6
|
+
if (!memoryExists(cwd)) {
|
|
7
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const worktreePath = createWorktree(cwd, options.branch, {
|
|
12
|
+
sessionId: options.sessionId,
|
|
13
|
+
agent: options.agent,
|
|
14
|
+
});
|
|
15
|
+
console.log(`✔ Worktree created: ${worktreePath}`);
|
|
16
|
+
console.log(` Branch: ${options.branch}`);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function runWorktreeList(options) {
|
|
24
|
+
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
25
|
+
if (!memoryExists(cwd)) {
|
|
26
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
const worktrees = listWorktrees(cwd);
|
|
30
|
+
if (worktrees.length === 0) {
|
|
31
|
+
console.log('No worktrees found.');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const managed = worktreesBaseDir(cwd);
|
|
35
|
+
console.log(`Worktrees (managed under ${managed}):\n`);
|
|
36
|
+
for (const wt of worktrees) {
|
|
37
|
+
const tag = wt.session_id ? ` [session: ${wt.session_id}]` : '';
|
|
38
|
+
const agentTag = wt.agent ? ` [agent: ${wt.agent}]` : '';
|
|
39
|
+
console.log(` ${wt.path}`);
|
|
40
|
+
console.log(` branch: ${wt.branch} commit: ${wt.commit.slice(0, 8)}${tag}${agentTag}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function runWorktreeRemove(options) {
|
|
44
|
+
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
45
|
+
if (!memoryExists(cwd)) {
|
|
46
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
removeWorktree(cwd, options.path, { force: options.force });
|
|
51
|
+
console.log(`✔ Worktree removed: ${options.path}`);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function runWorktreePrune(options) {
|
|
59
|
+
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
60
|
+
pruneWorktrees(cwd);
|
|
61
|
+
console.log('✔ Worktree stale entries pruned.');
|
|
62
|
+
}
|
|
63
|
+
export function runWorktreeClean(options) {
|
|
64
|
+
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
65
|
+
const result = cleanMergedWorktrees(cwd, {
|
|
66
|
+
force: options.force,
|
|
67
|
+
dryRun: options.dryRun,
|
|
68
|
+
});
|
|
69
|
+
if (result.removed.length === 0 && result.skipped.length === 0) {
|
|
70
|
+
console.log('✔ No merged or orphan worktrees to clean.');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const verb = options.dryRun ? 'Would remove' : 'Removed';
|
|
74
|
+
for (const p of result.removed) {
|
|
75
|
+
console.log(`${options.dryRun ? ' (dry-run)' : ' ✔'} ${verb}: ${p}`);
|
|
76
|
+
}
|
|
77
|
+
for (const s of result.skipped) {
|
|
78
|
+
console.log(` ⚠ Skipped: ${s.path} (${s.reason})`);
|
|
79
|
+
}
|
|
80
|
+
console.log(`\n${verb} ${result.removed.length} worktree(s), skipped ${result.skipped.length}.`);
|
|
81
|
+
}
|
|
82
|
+
export function runWorktreeMerge(options) {
|
|
83
|
+
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
84
|
+
const result = mergeWorktreeBranch(cwd, options.branch, {
|
|
85
|
+
message: options.message,
|
|
86
|
+
dryRun: options.dryRun,
|
|
87
|
+
});
|
|
88
|
+
if (!result.merged) {
|
|
89
|
+
if (options.dryRun) {
|
|
90
|
+
console.log(`(dry-run) Would merge ${options.branch}: ${result.filesChanged} files changed, ${result.filesRestored} parasitic deletions restored.`);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
console.error(`Error: ${result.error}`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
console.log(`✔ Merged ${options.branch} → ${result.commitHash}`);
|
|
99
|
+
console.log(` ${result.filesChanged} files changed, ${result.filesRestored} parasitic deletion(s) auto-restored.`);
|
|
100
|
+
}
|
|
101
|
+
/** Returns WorktreeInfo[] for use by MCP tools. */
|
|
102
|
+
export function getWorktrees(cwd) {
|
|
103
|
+
return listWorktrees(cwd);
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=worktree.js.map
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { JsonStore } from './json-store.js';
|
|
4
|
+
import { resolveEntityDir } from './io.js';
|
|
5
|
+
import { mutate } from './mutation-pipeline.js';
|
|
6
|
+
import { nowISO, generateIdWithLabel } from './ids.js';
|
|
7
|
+
import { ActionRequiredSchema, } from './schema.js';
|
|
8
|
+
import { saveVersionedJsonFile } from './migration.js';
|
|
9
|
+
import { appendAuditEntry } from './audit.js';
|
|
10
|
+
import { createRuntimeEvent } from './events.js';
|
|
11
|
+
import { loadAssignment, transitionAssignment } from './assignments.js';
|
|
12
|
+
import { loadAgentRun, transitionAgentRun } from './agentruns.js';
|
|
13
|
+
function actionsDir(cwd, mode = 'read') {
|
|
14
|
+
return resolveEntityDir('actions', cwd ?? process.cwd(), mode);
|
|
15
|
+
}
|
|
16
|
+
function ensureActionsDir(cwd) {
|
|
17
|
+
const dir = actionsDir(cwd, 'write');
|
|
18
|
+
if (!fs.existsSync(dir)) {
|
|
19
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function actionStore(cwd) {
|
|
23
|
+
return new JsonStore({
|
|
24
|
+
dirPath: actionsDir(cwd, 'read'),
|
|
25
|
+
documentType: 'action_required',
|
|
26
|
+
getId: (action) => action.id,
|
|
27
|
+
sort: (a, b) => a.created_at.localeCompare(b.created_at),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export function loadActionRequired(id, cwd) {
|
|
31
|
+
return actionStore(cwd).load(id);
|
|
32
|
+
}
|
|
33
|
+
/** Default TTL for pending actions (1 hour). */
|
|
34
|
+
const ACTION_DEFAULT_TTL_MS = 60 * 60 * 1000;
|
|
35
|
+
/**
|
|
36
|
+
* Expire stale pending actions on read.
|
|
37
|
+
* Called lazily during list() to avoid needing a separate sweeper process.
|
|
38
|
+
*/
|
|
39
|
+
function expireStaleActions(actions, cwd) {
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
for (const action of actions) {
|
|
42
|
+
if (action.status !== 'pending')
|
|
43
|
+
continue;
|
|
44
|
+
const expiresAt = action.expires_at
|
|
45
|
+
? new Date(action.expires_at).getTime()
|
|
46
|
+
: new Date(action.created_at).getTime() + ACTION_DEFAULT_TTL_MS;
|
|
47
|
+
if (now > expiresAt) {
|
|
48
|
+
action.status = 'expired';
|
|
49
|
+
action.updated_at = nowISO();
|
|
50
|
+
try {
|
|
51
|
+
saveActionRequired(action, cwd);
|
|
52
|
+
}
|
|
53
|
+
catch { /* best-effort */ }
|
|
54
|
+
const expiryReason = `Action required expired: ${action.title}`;
|
|
55
|
+
try {
|
|
56
|
+
const run = action.run_id ? loadAgentRun(action.run_id, cwd) : undefined;
|
|
57
|
+
if (run && !['completed', 'failed', 'cancelled', 'timed_out', 'interrupted'].includes(run.status)) {
|
|
58
|
+
transitionAgentRun(run.id, 'timed_out', {
|
|
59
|
+
actor: action.agent,
|
|
60
|
+
actor_id: action.agent_id,
|
|
61
|
+
session_id: action.session_id,
|
|
62
|
+
status_reason: expiryReason,
|
|
63
|
+
}, cwd);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch { /* best-effort */ }
|
|
67
|
+
try {
|
|
68
|
+
const assignment = loadAssignment(action.assignment_id, cwd);
|
|
69
|
+
if (assignment && assignment.status === 'blocked') {
|
|
70
|
+
transitionAssignment(assignment.id, 'failed', {
|
|
71
|
+
actor: action.agent,
|
|
72
|
+
actor_id: action.agent_id,
|
|
73
|
+
session_id: action.session_id,
|
|
74
|
+
status_reason: expiryReason,
|
|
75
|
+
error_message: expiryReason,
|
|
76
|
+
}, cwd);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch { /* best-effort */ }
|
|
80
|
+
try {
|
|
81
|
+
appendAuditEntry({
|
|
82
|
+
actor: action.agent,
|
|
83
|
+
actor_id: action.agent_id,
|
|
84
|
+
action: 'update',
|
|
85
|
+
item_id: action.id,
|
|
86
|
+
item_type: 'state',
|
|
87
|
+
before: { status: 'pending' },
|
|
88
|
+
after: { status: 'expired' },
|
|
89
|
+
scope: action.scope,
|
|
90
|
+
session_id: action.session_id,
|
|
91
|
+
}, cwd);
|
|
92
|
+
}
|
|
93
|
+
catch { /* best-effort */ }
|
|
94
|
+
try {
|
|
95
|
+
createRuntimeEvent({
|
|
96
|
+
agent: action.agent,
|
|
97
|
+
agent_id: action.agent_id,
|
|
98
|
+
session_id: action.session_id,
|
|
99
|
+
event_type: 'observation',
|
|
100
|
+
text: `Action expired: ${action.title}`,
|
|
101
|
+
tags: ['agent-runtime', 'action-expired', `kind:${action.kind}`],
|
|
102
|
+
assignment_id: action.assignment_id,
|
|
103
|
+
run_id: action.run_id,
|
|
104
|
+
claim_id: action.claim_id,
|
|
105
|
+
plan_id: action.plan_id,
|
|
106
|
+
sequence_id: action.sequence_id,
|
|
107
|
+
scope: action.scope,
|
|
108
|
+
status: action.status,
|
|
109
|
+
status_reason: expiryReason,
|
|
110
|
+
metadata: {
|
|
111
|
+
protocol: 'brainclaw.agent_runtime.action_required.v1',
|
|
112
|
+
action_id: action.id,
|
|
113
|
+
outcome: 'expired',
|
|
114
|
+
},
|
|
115
|
+
}, cwd);
|
|
116
|
+
}
|
|
117
|
+
catch { /* best-effort */ }
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return actions;
|
|
121
|
+
}
|
|
122
|
+
export function listActionRequired(cwd, filter = {}) {
|
|
123
|
+
let actions = actionStore(cwd).list();
|
|
124
|
+
// Sweep-on-read: expire stale pending actions
|
|
125
|
+
actions = expireStaleActions(actions, cwd);
|
|
126
|
+
if (filter.status)
|
|
127
|
+
actions = actions.filter((action) => action.status === filter.status);
|
|
128
|
+
if (filter.kind)
|
|
129
|
+
actions = actions.filter((action) => action.kind === filter.kind);
|
|
130
|
+
if (filter.agent)
|
|
131
|
+
actions = actions.filter((action) => action.agent === filter.agent);
|
|
132
|
+
if (filter.assignment_id)
|
|
133
|
+
actions = actions.filter((action) => action.assignment_id === filter.assignment_id);
|
|
134
|
+
if (filter.run_id)
|
|
135
|
+
actions = actions.filter((action) => action.run_id === filter.run_id);
|
|
136
|
+
if (filter.claim_id)
|
|
137
|
+
actions = actions.filter((action) => action.claim_id === filter.claim_id);
|
|
138
|
+
if (filter.plan_id)
|
|
139
|
+
actions = actions.filter((action) => action.plan_id === filter.plan_id);
|
|
140
|
+
if (filter.sequence_id)
|
|
141
|
+
actions = actions.filter((action) => action.sequence_id === filter.sequence_id);
|
|
142
|
+
return actions;
|
|
143
|
+
}
|
|
144
|
+
function saveActionRequired(action, cwd) {
|
|
145
|
+
mutate({ cwd }, () => {
|
|
146
|
+
ensureActionsDir(cwd);
|
|
147
|
+
const filepath = path.join(actionsDir(cwd, 'write'), `${action.id}.json`);
|
|
148
|
+
saveVersionedJsonFile('action_required', filepath, ActionRequiredSchema.parse(action));
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
export function createActionRequired(options, cwd) {
|
|
152
|
+
const generated = generateIdWithLabel('actions', cwd);
|
|
153
|
+
const now = nowISO();
|
|
154
|
+
const action = ActionRequiredSchema.parse({
|
|
155
|
+
schema_version: 1,
|
|
156
|
+
id: generated.id,
|
|
157
|
+
short_label: generated.short_label,
|
|
158
|
+
assignment_id: options.assignment_id,
|
|
159
|
+
run_id: options.run_id,
|
|
160
|
+
claim_id: options.claim_id,
|
|
161
|
+
message_id: options.message_id,
|
|
162
|
+
plan_id: options.plan_id,
|
|
163
|
+
sequence_id: options.sequence_id,
|
|
164
|
+
agent: options.agent,
|
|
165
|
+
agent_id: options.agent_id,
|
|
166
|
+
session_id: options.session_id,
|
|
167
|
+
kind: options.kind,
|
|
168
|
+
status: 'pending',
|
|
169
|
+
scope: options.scope,
|
|
170
|
+
title: options.title,
|
|
171
|
+
prompt: options.prompt,
|
|
172
|
+
options: options.options ?? [],
|
|
173
|
+
response_schema: options.response_schema,
|
|
174
|
+
created_at: now,
|
|
175
|
+
updated_at: now,
|
|
176
|
+
expires_at: new Date(Date.now() + (options.ttl_ms ?? ACTION_DEFAULT_TTL_MS)).toISOString(),
|
|
177
|
+
tags: options.tags ?? [],
|
|
178
|
+
});
|
|
179
|
+
saveActionRequired(action, cwd);
|
|
180
|
+
appendAuditEntry({
|
|
181
|
+
actor: options.agent,
|
|
182
|
+
actor_id: options.agent_id,
|
|
183
|
+
action: 'create',
|
|
184
|
+
item_id: action.id,
|
|
185
|
+
item_type: 'state',
|
|
186
|
+
after: { kind: action.kind, assignment_id: action.assignment_id, run_id: action.run_id },
|
|
187
|
+
scope: action.scope,
|
|
188
|
+
session_id: action.session_id,
|
|
189
|
+
}, cwd);
|
|
190
|
+
createRuntimeEvent({
|
|
191
|
+
agent: options.agent,
|
|
192
|
+
agent_id: options.agent_id,
|
|
193
|
+
session_id: options.session_id,
|
|
194
|
+
event_type: 'observation',
|
|
195
|
+
text: `Action required: ${action.title}`,
|
|
196
|
+
tags: ['agent-runtime', 'action-required', `kind:${action.kind}`],
|
|
197
|
+
assignment_id: action.assignment_id,
|
|
198
|
+
run_id: action.run_id,
|
|
199
|
+
claim_id: action.claim_id,
|
|
200
|
+
plan_id: action.plan_id,
|
|
201
|
+
sequence_id: action.sequence_id,
|
|
202
|
+
scope: action.scope,
|
|
203
|
+
status: action.status,
|
|
204
|
+
status_reason: action.prompt,
|
|
205
|
+
metadata: {
|
|
206
|
+
protocol: 'brainclaw.agent_runtime.action_required.v1',
|
|
207
|
+
action_id: action.id,
|
|
208
|
+
kind: action.kind,
|
|
209
|
+
},
|
|
210
|
+
}, cwd);
|
|
211
|
+
return action;
|
|
212
|
+
}
|
|
213
|
+
export function resolveActionRequired(id, options, cwd) {
|
|
214
|
+
// Wrap the entire load-check-mutate-save cycle in mutate() to prevent
|
|
215
|
+
// TOCTOU races where two supervisors resolve the same action concurrently.
|
|
216
|
+
// The inner saveActionRequired also calls mutate(), but the lock is reentrant.
|
|
217
|
+
return mutate({ cwd }, () => {
|
|
218
|
+
const action = loadActionRequired(id, cwd);
|
|
219
|
+
if (!action) {
|
|
220
|
+
throw new Error(`ActionRequired not found: ${id}`);
|
|
221
|
+
}
|
|
222
|
+
if (action.status !== 'pending') {
|
|
223
|
+
throw new Error(`ActionRequired ${id} is already ${action.status}`);
|
|
224
|
+
}
|
|
225
|
+
const now = nowISO();
|
|
226
|
+
action.status = options.outcome;
|
|
227
|
+
action.updated_at = now;
|
|
228
|
+
action.resolved_at = now;
|
|
229
|
+
action.response = {
|
|
230
|
+
outcome: options.outcome,
|
|
231
|
+
text: options.text,
|
|
232
|
+
payload: options.payload,
|
|
233
|
+
responded_by: options.responded_by,
|
|
234
|
+
responded_by_id: options.responded_by_id,
|
|
235
|
+
responded_at: now,
|
|
236
|
+
};
|
|
237
|
+
saveActionRequired(action, cwd);
|
|
238
|
+
appendAuditEntry({
|
|
239
|
+
actor: options.responded_by,
|
|
240
|
+
actor_id: options.responded_by_id,
|
|
241
|
+
action: 'update',
|
|
242
|
+
item_id: action.id,
|
|
243
|
+
item_type: 'state',
|
|
244
|
+
before: { status: 'pending' },
|
|
245
|
+
after: { status: action.status },
|
|
246
|
+
scope: action.scope,
|
|
247
|
+
session_id: options.session_id,
|
|
248
|
+
}, cwd);
|
|
249
|
+
const runtimeStatusMessage = options.text ?? `${action.kind} ${options.outcome}`;
|
|
250
|
+
if (action.run_id) {
|
|
251
|
+
const run = loadAgentRun(action.run_id, cwd);
|
|
252
|
+
if (run) {
|
|
253
|
+
if (options.outcome === 'resolved' && ['blocked', 'waiting_input'].includes(run.status)) {
|
|
254
|
+
transitionAgentRun(run.id, 'running', {
|
|
255
|
+
actor: options.responded_by,
|
|
256
|
+
actor_id: options.responded_by_id,
|
|
257
|
+
session_id: options.session_id,
|
|
258
|
+
status_reason: runtimeStatusMessage,
|
|
259
|
+
}, cwd);
|
|
260
|
+
}
|
|
261
|
+
else if (options.outcome !== 'resolved' && !['completed', 'failed', 'cancelled', 'timed_out', 'interrupted'].includes(run.status)) {
|
|
262
|
+
transitionAgentRun(run.id, 'cancelled', {
|
|
263
|
+
actor: options.responded_by,
|
|
264
|
+
actor_id: options.responded_by_id,
|
|
265
|
+
session_id: options.session_id,
|
|
266
|
+
status_reason: runtimeStatusMessage,
|
|
267
|
+
}, cwd);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const assignment = loadAssignment(action.assignment_id, cwd);
|
|
272
|
+
if (assignment) {
|
|
273
|
+
if (options.outcome === 'resolved' && assignment.status === 'blocked') {
|
|
274
|
+
transitionAssignment(assignment.id, 'started', {
|
|
275
|
+
actor: options.responded_by,
|
|
276
|
+
actor_id: options.responded_by_id,
|
|
277
|
+
session_id: options.session_id,
|
|
278
|
+
status_reason: runtimeStatusMessage,
|
|
279
|
+
}, cwd);
|
|
280
|
+
}
|
|
281
|
+
else if (options.outcome !== 'resolved' && assignment.status === 'blocked') {
|
|
282
|
+
transitionAssignment(assignment.id, 'failed', {
|
|
283
|
+
actor: options.responded_by,
|
|
284
|
+
actor_id: options.responded_by_id,
|
|
285
|
+
session_id: options.session_id,
|
|
286
|
+
status_reason: runtimeStatusMessage,
|
|
287
|
+
error_message: runtimeStatusMessage,
|
|
288
|
+
}, cwd);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
createRuntimeEvent({
|
|
292
|
+
agent: options.responded_by,
|
|
293
|
+
agent_id: options.responded_by_id,
|
|
294
|
+
session_id: options.session_id,
|
|
295
|
+
event_type: 'observation',
|
|
296
|
+
text: `Action ${options.outcome}: ${action.title}`,
|
|
297
|
+
tags: ['agent-runtime', 'action-response', `kind:${action.kind}`],
|
|
298
|
+
assignment_id: action.assignment_id,
|
|
299
|
+
run_id: action.run_id,
|
|
300
|
+
claim_id: action.claim_id,
|
|
301
|
+
plan_id: action.plan_id,
|
|
302
|
+
sequence_id: action.sequence_id,
|
|
303
|
+
scope: action.scope,
|
|
304
|
+
status: action.status,
|
|
305
|
+
status_reason: runtimeStatusMessage,
|
|
306
|
+
metadata: {
|
|
307
|
+
protocol: 'brainclaw.agent_runtime.action_required.v1',
|
|
308
|
+
action_id: action.id,
|
|
309
|
+
outcome: options.outcome,
|
|
310
|
+
},
|
|
311
|
+
}, cwd);
|
|
312
|
+
return action;
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=actions.js.map
|