brainclaw 0.19.2
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/LICENSE +74 -0
- package/README.md +226 -0
- package/dist/cli.js +1037 -0
- package/dist/commands/accept.js +149 -0
- package/dist/commands/adapter-openclaw-import.js +75 -0
- package/dist/commands/add-step.js +35 -0
- package/dist/commands/agent-board.js +106 -0
- package/dist/commands/audit.js +35 -0
- package/dist/commands/bootstrap.js +34 -0
- package/dist/commands/capability.js +104 -0
- package/dist/commands/changes.js +112 -0
- package/dist/commands/check-constraints.js +63 -0
- package/dist/commands/claim-resource.js +54 -0
- package/dist/commands/claim.js +92 -0
- package/dist/commands/complete-step.js +34 -0
- package/dist/commands/constraint.js +44 -0
- package/dist/commands/context-diff.js +32 -0
- package/dist/commands/context.js +63 -0
- package/dist/commands/decision.js +45 -0
- package/dist/commands/delete-plan.js +20 -0
- package/dist/commands/diff.js +99 -0
- package/dist/commands/doctor.js +1275 -0
- package/dist/commands/enable-agent.js +63 -0
- package/dist/commands/env.js +46 -0
- package/dist/commands/estimation-report.js +167 -0
- package/dist/commands/explore.js +47 -0
- package/dist/commands/export.js +381 -0
- package/dist/commands/handoff.js +63 -0
- package/dist/commands/history.js +22 -0
- package/dist/commands/hooks.js +123 -0
- package/dist/commands/init.js +356 -0
- package/dist/commands/install-hooks.js +115 -0
- package/dist/commands/instruction.js +56 -0
- package/dist/commands/list-agents.js +44 -0
- package/dist/commands/list-claims.js +45 -0
- package/dist/commands/list-instructions.js +50 -0
- package/dist/commands/list-plans.js +48 -0
- package/dist/commands/mcp-worker.js +12 -0
- package/dist/commands/mcp.js +2272 -0
- package/dist/commands/memory.js +283 -0
- package/dist/commands/metrics.js +175 -0
- package/dist/commands/plan-resource.js +62 -0
- package/dist/commands/plan.js +76 -0
- package/dist/commands/prune-candidates.js +36 -0
- package/dist/commands/prune.js +48 -0
- package/dist/commands/pull.js +25 -0
- package/dist/commands/push.js +28 -0
- package/dist/commands/rebuild.js +14 -0
- package/dist/commands/reflect-runtime-note.js +74 -0
- package/dist/commands/reflect.js +286 -0
- package/dist/commands/register-agent.js +29 -0
- package/dist/commands/reject.js +52 -0
- package/dist/commands/release-claim.js +41 -0
- package/dist/commands/release-claims.js +67 -0
- package/dist/commands/review.js +242 -0
- package/dist/commands/rollback.js +156 -0
- package/dist/commands/runtime-note.js +144 -0
- package/dist/commands/runtime-status.js +49 -0
- package/dist/commands/search.js +36 -0
- package/dist/commands/session-end.js +187 -0
- package/dist/commands/session-start.js +147 -0
- package/dist/commands/set-trust.js +92 -0
- package/dist/commands/setup.js +446 -0
- package/dist/commands/show-candidate.js +31 -0
- package/dist/commands/star-candidate.js +28 -0
- package/dist/commands/status.js +133 -0
- package/dist/commands/sync.js +159 -0
- package/dist/commands/tool.js +126 -0
- package/dist/commands/trap.js +74 -0
- package/dist/commands/update-handoff.js +23 -0
- package/dist/commands/update-plan.js +37 -0
- package/dist/commands/upgrade.js +382 -0
- package/dist/commands/use-candidate.js +35 -0
- package/dist/commands/version.js +96 -0
- package/dist/commands/watch.js +215 -0
- package/dist/commands/whoami.js +104 -0
- package/dist/core/agent-context.js +340 -0
- package/dist/core/agent-files.js +874 -0
- package/dist/core/agent-integrations.js +135 -0
- package/dist/core/agent-inventory.js +401 -0
- package/dist/core/agent-registry.js +420 -0
- package/dist/core/ai-agent-detection.js +140 -0
- package/dist/core/audit.js +85 -0
- package/dist/core/bootstrap.js +658 -0
- package/dist/core/brainclaw-version.js +433 -0
- package/dist/core/candidates.js +137 -0
- package/dist/core/circuit-breaker.js +118 -0
- package/dist/core/claims.js +72 -0
- package/dist/core/config.js +86 -0
- package/dist/core/context-diff.js +122 -0
- package/dist/core/context.js +1212 -0
- package/dist/core/contradictions.js +270 -0
- package/dist/core/coordination.js +86 -0
- package/dist/core/cross-project.js +99 -0
- package/dist/core/duplicates.js +72 -0
- package/dist/core/event-log.js +152 -0
- package/dist/core/events.js +56 -0
- package/dist/core/execution-context.js +204 -0
- package/dist/core/freshness.js +87 -0
- package/dist/core/global-registry.js +182 -0
- package/dist/core/host.js +10 -0
- package/dist/core/identity.js +151 -0
- package/dist/core/ids.js +56 -0
- package/dist/core/input-validation.js +81 -0
- package/dist/core/instructions.js +117 -0
- package/dist/core/io.js +191 -0
- package/dist/core/json-store.js +63 -0
- package/dist/core/lifecycle.js +45 -0
- package/dist/core/lock.js +129 -0
- package/dist/core/logger.js +49 -0
- package/dist/core/machine-profile.js +332 -0
- package/dist/core/markdown.js +120 -0
- package/dist/core/memory-git.js +133 -0
- package/dist/core/migration.js +247 -0
- package/dist/core/project-registry.js +64 -0
- package/dist/core/reflection-safety.js +21 -0
- package/dist/core/repo-analysis.js +133 -0
- package/dist/core/reputation.js +409 -0
- package/dist/core/runtime.js +134 -0
- package/dist/core/schema.js +580 -0
- package/dist/core/search.js +115 -0
- package/dist/core/security.js +66 -0
- package/dist/core/setup-state.js +50 -0
- package/dist/core/state.js +83 -0
- package/dist/core/store-resolution.js +119 -0
- package/dist/core/sync-remote.js +83 -0
- package/dist/core/traps.js +86 -0
- package/package.json +60 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { memoryExists } from '../core/io.js';
|
|
3
|
+
import { buildOperationalIdentity, clearCurrentSession } from '../core/identity.js';
|
|
4
|
+
import { buildContextDiff } from '../core/context-diff.js';
|
|
5
|
+
import { listClaims, releaseClaim } from '../core/claims.js';
|
|
6
|
+
import { listRuntimeNotes, saveRuntimeNote, generateRuntimeNoteId } from '../core/runtime.js';
|
|
7
|
+
import { loadState } from '../core/state.js';
|
|
8
|
+
import { createCandidateFromInput } from './reflect.js';
|
|
9
|
+
import { suggestCandidateTypes } from './reflect-runtime-note.js';
|
|
10
|
+
import { nowISO } from '../core/ids.js';
|
|
11
|
+
import { appendAuditEntry } from '../core/audit.js';
|
|
12
|
+
import { requireMinimumTrustLevel, requireRegisteredAgentIdentity } from '../core/agent-registry.js';
|
|
13
|
+
import { loadSessionSnapshot } from '../commands/session-start.js';
|
|
14
|
+
export function runSessionEnd(options = {}) {
|
|
15
|
+
try {
|
|
16
|
+
const result = endSession(options);
|
|
17
|
+
if (options.json) {
|
|
18
|
+
console.log(JSON.stringify(result, null, 2));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (result.open_work_warning) {
|
|
22
|
+
const w = result.open_work_warning;
|
|
23
|
+
console.log('⚠ Open work detected at session end:');
|
|
24
|
+
for (const c of w.active_claims) {
|
|
25
|
+
console.log(` claim [${c.id}] ${c.description}`);
|
|
26
|
+
console.log(` scope: ${c.scope}`);
|
|
27
|
+
}
|
|
28
|
+
for (const p of w.in_progress_plans) {
|
|
29
|
+
console.log(` plan [${p.id}] ${p.text}`);
|
|
30
|
+
}
|
|
31
|
+
if (w.auto_released) {
|
|
32
|
+
console.log(' → Claims auto-released and plans left for manual update.');
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
console.log(' → Run `brainclaw claim release <id>` and `brainclaw plan update <id> --status done` to clean up.');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
console.log(`✔ Session ended: ${result.session_id} (${result.agent})`);
|
|
39
|
+
console.log(` Runtime notes in session: ${result.notes_in_session}`);
|
|
40
|
+
if (options.autoReflect) {
|
|
41
|
+
console.log(` Candidates created from auto-reflect: ${result.candidates_created}`);
|
|
42
|
+
}
|
|
43
|
+
if (result.context_diff) {
|
|
44
|
+
console.log(` ${result.context_diff}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function endSession(options = {}) {
|
|
53
|
+
if (!memoryExists(options.cwd)) {
|
|
54
|
+
throw new Error('.brainclaw/ not found. Run `brainclaw init` first.');
|
|
55
|
+
}
|
|
56
|
+
const registered = requireRegisteredAgentIdentity({
|
|
57
|
+
agentName: options.agent,
|
|
58
|
+
agentId: options.agentId,
|
|
59
|
+
cwd: options.cwd,
|
|
60
|
+
allowCurrent: true,
|
|
61
|
+
allowEnv: true,
|
|
62
|
+
});
|
|
63
|
+
requireMinimumTrustLevel(registered, 'contributor');
|
|
64
|
+
const actor = buildOperationalIdentity(registered.agent_name, options.cwd, { agentId: registered.agent_id });
|
|
65
|
+
const sessionId = options.session ?? actor.session_id;
|
|
66
|
+
if (!sessionId) {
|
|
67
|
+
throw new Error('no session ID provided. Use --session <id> or set BRAINCLAW_SESSION_ID.');
|
|
68
|
+
}
|
|
69
|
+
// Hygiene check: find open work belonging to this agent
|
|
70
|
+
const allClaims = listClaims(options.cwd);
|
|
71
|
+
const activeClaims = allClaims.filter((c) => c.status === 'active' && (registered.agent_id ? c.agent_id === registered.agent_id : c.agent === registered.agent_name));
|
|
72
|
+
const state = loadState(options.cwd);
|
|
73
|
+
const claimPlanIds = new Set(activeClaims.map((c) => c.plan_id).filter(Boolean));
|
|
74
|
+
const inProgressPlans = state.plan_items.filter((p) => p.status === 'in_progress' && (p.assignee === registered.agent_name || claimPlanIds.has(p.id)));
|
|
75
|
+
let openWorkWarning;
|
|
76
|
+
if (activeClaims.length > 0 || inProgressPlans.length > 0) {
|
|
77
|
+
if (options.autoRelease) {
|
|
78
|
+
for (const c of activeClaims) {
|
|
79
|
+
releaseClaim(c.id, options.cwd);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
openWorkWarning = {
|
|
83
|
+
active_claims: activeClaims.map(({ id, scope, description }) => ({ id, scope, description })),
|
|
84
|
+
in_progress_plans: inProgressPlans.map(({ id, text }) => ({ id, text })),
|
|
85
|
+
auto_released: options.autoRelease ?? false,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// Get session notes for summary
|
|
89
|
+
const agentNotes = listRuntimeNotes(actor.agent, options.cwd);
|
|
90
|
+
const sessionNotes = agentNotes.filter(n => n.session_id === sessionId);
|
|
91
|
+
const diff = buildContextDiff({
|
|
92
|
+
session: sessionId,
|
|
93
|
+
cwd: options.cwd,
|
|
94
|
+
includeItems: true,
|
|
95
|
+
});
|
|
96
|
+
const contextDiff = diff?.summary;
|
|
97
|
+
const summaryText = options.summary
|
|
98
|
+
? options.summary
|
|
99
|
+
: `Session ended — ${sessionNotes.length} runtime note(s) created`;
|
|
100
|
+
// Write session_end runtime note
|
|
101
|
+
const noteId = generateRuntimeNoteId();
|
|
102
|
+
saveRuntimeNote({
|
|
103
|
+
id: noteId,
|
|
104
|
+
agent: actor.agent,
|
|
105
|
+
agent_id: actor.agent_id,
|
|
106
|
+
project_id: actor.project_id,
|
|
107
|
+
session_id: sessionId,
|
|
108
|
+
text: summaryText + (contextDiff ? `\n${contextDiff}` : ''),
|
|
109
|
+
created_at: nowISO(),
|
|
110
|
+
tags: ['session'],
|
|
111
|
+
visibility: 'shared',
|
|
112
|
+
note_type: 'session_end',
|
|
113
|
+
}, options.cwd);
|
|
114
|
+
appendAuditEntry({ action: 'session_end', actor: actor.agent, actor_id: actor.agent_id, item_id: sessionId, item_type: 'session' }, options.cwd);
|
|
115
|
+
clearCurrentSession(options.cwd, sessionId);
|
|
116
|
+
// Reflect-handoff: generate a handoff candidate from git commits since session start
|
|
117
|
+
if (options.reflectHandoff) {
|
|
118
|
+
try {
|
|
119
|
+
const snapshot = loadSessionSnapshot(sessionId, options.cwd);
|
|
120
|
+
const startSha = snapshot?.git_sha;
|
|
121
|
+
const ref = startSha ?? 'HEAD~10';
|
|
122
|
+
const cwd = options.cwd ?? process.cwd();
|
|
123
|
+
const commits = execSync(`git log --oneline ${ref}..HEAD`, { encoding: 'utf-8', cwd }).trim();
|
|
124
|
+
const diffStat = execSync(`git diff --stat ${ref}..HEAD`, { encoding: 'utf-8', cwd }).trim();
|
|
125
|
+
if (commits) {
|
|
126
|
+
const releasedScopes = listClaims(options.cwd)
|
|
127
|
+
.filter((c) => c.status === 'released' && c.agent === registered.agent_name)
|
|
128
|
+
.map((c) => c.scope)
|
|
129
|
+
.join(', ');
|
|
130
|
+
const handoffText = [
|
|
131
|
+
`Session ${sessionId} — auto-generated handoff`,
|
|
132
|
+
'',
|
|
133
|
+
`Commits:\n${commits}`,
|
|
134
|
+
diffStat ? `\nChanged files:\n${diffStat}` : '',
|
|
135
|
+
releasedScopes ? `\nReleased claims: ${releasedScopes}` : '',
|
|
136
|
+
summaryText !== `Session ended — ${sessionNotes.length} runtime note(s) created` ? `\nSummary: ${summaryText}` : '',
|
|
137
|
+
].filter(Boolean).join('\n');
|
|
138
|
+
createCandidateFromInput(handoffText, 'handoff', {
|
|
139
|
+
author: actor.agent,
|
|
140
|
+
authorId: actor.agent_id,
|
|
141
|
+
sessionId,
|
|
142
|
+
source: `session-end:git-diff:${sessionId}`,
|
|
143
|
+
cwd: options.cwd,
|
|
144
|
+
}, false, false, true);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch { /* non-fatal — no git or no commits */ }
|
|
148
|
+
}
|
|
149
|
+
// Auto-reflect: generate candidates from session notes
|
|
150
|
+
let candidatesCreated = 0;
|
|
151
|
+
if (options.autoReflect && sessionNotes.length > 0) {
|
|
152
|
+
for (const note of sessionNotes) {
|
|
153
|
+
if (note.note_type === 'observation' || !note.note_type) {
|
|
154
|
+
try {
|
|
155
|
+
const detected = suggestCandidateTypes(note.text, note.tags).find((entry) => entry.type !== 'handoff');
|
|
156
|
+
if (!detected || detected.score < 4) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const creation = createCandidateFromInput(note.text, detected.type, {
|
|
160
|
+
tag: note.tags,
|
|
161
|
+
author: note.agent,
|
|
162
|
+
authorId: note.agent_id,
|
|
163
|
+
projectId: note.project_id,
|
|
164
|
+
sessionId: note.session_id,
|
|
165
|
+
source: `runtime-note:${note.agent}:${note.id}`,
|
|
166
|
+
cwd: options.cwd,
|
|
167
|
+
}, false, false, true);
|
|
168
|
+
if (creation.candidateId) {
|
|
169
|
+
candidatesCreated++;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch { /* skip */ }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const result = {
|
|
177
|
+
session_id: sessionId,
|
|
178
|
+
agent: actor.agent,
|
|
179
|
+
notes_in_session: sessionNotes.length,
|
|
180
|
+
candidates_created: candidatesCreated,
|
|
181
|
+
context_diff: contextDiff,
|
|
182
|
+
summary: summaryText,
|
|
183
|
+
open_work_warning: openWorkWarning,
|
|
184
|
+
};
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=session-end.js.map
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
import { memoryExists, resolveEntityDir } from '../core/io.js';
|
|
5
|
+
import { loadVersionedJsonFile, saveVersionedJsonFile } from '../core/migration.js';
|
|
6
|
+
import { buildOperationalIdentity, saveCurrentSession } from '../core/identity.js';
|
|
7
|
+
import { requireMinimumTrustLevel, requireRegisteredAgentIdentity, resolveCurrentModel } from '../core/agent-registry.js';
|
|
8
|
+
import { buildContext } from '../core/context.js';
|
|
9
|
+
import { saveRuntimeNote, generateRuntimeNoteId } from '../core/runtime.js';
|
|
10
|
+
import { nowISO, generateId } from '../core/ids.js';
|
|
11
|
+
import { appendAuditEntry } from '../core/audit.js';
|
|
12
|
+
import { SessionSnapshotSchema } from '../core/schema.js';
|
|
13
|
+
import { auditLocalAgentWorkspaceFiles } from '../core/agent-files.js';
|
|
14
|
+
function sessionsDir(cwd) {
|
|
15
|
+
return resolveEntityDir('sessions', cwd ?? process.cwd(), 'read');
|
|
16
|
+
}
|
|
17
|
+
function sessionSnapshotPath(sessionId, cwd) {
|
|
18
|
+
return path.join(sessionsDir(cwd), `${sessionId}.json`);
|
|
19
|
+
}
|
|
20
|
+
function createHash(data) {
|
|
21
|
+
let hash = 0;
|
|
22
|
+
for (let i = 0; i < data.length; i++) {
|
|
23
|
+
const chr = data.charCodeAt(i);
|
|
24
|
+
hash = ((hash << 5) - hash) + chr;
|
|
25
|
+
hash |= 0;
|
|
26
|
+
}
|
|
27
|
+
return (hash >>> 0).toString(16).padStart(8, '0');
|
|
28
|
+
}
|
|
29
|
+
export function runSessionStart(options = {}) {
|
|
30
|
+
try {
|
|
31
|
+
const snapshot = startSession(options);
|
|
32
|
+
if (options.json) {
|
|
33
|
+
console.log(JSON.stringify(snapshot, null, 2));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
console.log(`✔ Session started: ${snapshot.session_id} (${snapshot.agent})`);
|
|
37
|
+
if (options.context)
|
|
38
|
+
console.log(` Context target: ${options.context}`);
|
|
39
|
+
if (snapshot.agent_git_hygiene && (snapshot.agent_git_hygiene.missing_gitignore_paths.length > 0 || snapshot.agent_git_hygiene.tracked_paths.length > 0)) {
|
|
40
|
+
console.warn('⚠ Local Brainclaw agent files in this repo should stay unversioned.');
|
|
41
|
+
if (snapshot.agent_git_hygiene.missing_gitignore_paths.length > 0) {
|
|
42
|
+
console.warn(` Missing .gitignore entries: ${snapshot.agent_git_hygiene.missing_gitignore_paths.join(', ')}`);
|
|
43
|
+
console.warn(' Fix: run `brainclaw doctor --fix-agent-ignore`');
|
|
44
|
+
}
|
|
45
|
+
if (snapshot.agent_git_hygiene.tracked_paths.length > 0) {
|
|
46
|
+
console.warn(` Tracked local agent files: ${snapshot.agent_git_hygiene.tracked_paths.join(', ')}`);
|
|
47
|
+
console.warn(' After fixing .gitignore, untrack them with `git rm --cached <path>` as needed.');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function startSession(options = {}) {
|
|
57
|
+
if (!memoryExists(options.cwd)) {
|
|
58
|
+
throw new Error('.brainclaw/ not found. Run `brainclaw init` first.');
|
|
59
|
+
}
|
|
60
|
+
const registered = requireRegisteredAgentIdentity({
|
|
61
|
+
agentName: options.agent,
|
|
62
|
+
agentId: options.agentId,
|
|
63
|
+
cwd: options.cwd,
|
|
64
|
+
allowCurrent: true,
|
|
65
|
+
allowEnv: true,
|
|
66
|
+
});
|
|
67
|
+
requireMinimumTrustLevel(registered, 'contributor');
|
|
68
|
+
const actor = buildOperationalIdentity(registered.agent_name, options.cwd, { agentId: registered.agent_id });
|
|
69
|
+
// Capture initial context snapshot
|
|
70
|
+
let initialContextHash;
|
|
71
|
+
try {
|
|
72
|
+
const ctx = buildContext({ target: options.context, agent: actor.agent, cwd: options.cwd });
|
|
73
|
+
initialContextHash = createHash(JSON.stringify(ctx.selected));
|
|
74
|
+
}
|
|
75
|
+
catch { /* non-fatal */ }
|
|
76
|
+
// Capture git HEAD SHA for later handoff generation
|
|
77
|
+
let gitSha;
|
|
78
|
+
try {
|
|
79
|
+
gitSha = execSync('git rev-parse HEAD', { encoding: 'utf-8', cwd: options.cwd ?? process.cwd() }).trim();
|
|
80
|
+
}
|
|
81
|
+
catch { /* non-fatal — not a git repo */ }
|
|
82
|
+
const model = options.model ?? resolveCurrentModel(options.cwd);
|
|
83
|
+
const snapshot = {
|
|
84
|
+
schema_version: 2,
|
|
85
|
+
session_id: actor.session_id ?? generateId('sessions'),
|
|
86
|
+
agent: actor.agent,
|
|
87
|
+
agent_id: actor.agent_id,
|
|
88
|
+
started_at: nowISO(),
|
|
89
|
+
context_target: options.context,
|
|
90
|
+
initial_context_hash: initialContextHash,
|
|
91
|
+
git_sha: gitSha,
|
|
92
|
+
...(model ? { model } : {}),
|
|
93
|
+
};
|
|
94
|
+
// Persist snapshot
|
|
95
|
+
const dir = sessionsDir(options.cwd);
|
|
96
|
+
if (!fs.existsSync(dir))
|
|
97
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
98
|
+
saveVersionedJsonFile('session_snapshot', sessionSnapshotPath(snapshot.session_id, options.cwd), SessionSnapshotSchema.parse(snapshot));
|
|
99
|
+
saveCurrentSession({
|
|
100
|
+
schema_version: 2,
|
|
101
|
+
session_id: snapshot.session_id,
|
|
102
|
+
started_at: snapshot.started_at,
|
|
103
|
+
last_seen_at: snapshot.started_at,
|
|
104
|
+
agent: actor.agent,
|
|
105
|
+
agent_id: actor.agent_id,
|
|
106
|
+
host_id: actor.host_id,
|
|
107
|
+
}, options.cwd);
|
|
108
|
+
// Write session_start runtime note
|
|
109
|
+
const noteId = generateRuntimeNoteId();
|
|
110
|
+
saveRuntimeNote({
|
|
111
|
+
id: noteId,
|
|
112
|
+
agent: actor.agent,
|
|
113
|
+
agent_id: actor.agent_id,
|
|
114
|
+
project_id: actor.project_id,
|
|
115
|
+
session_id: snapshot.session_id,
|
|
116
|
+
text: `Session started${options.context ? ` — context: ${options.context}` : ''}`,
|
|
117
|
+
created_at: nowISO(),
|
|
118
|
+
tags: ['session'],
|
|
119
|
+
visibility: 'shared',
|
|
120
|
+
note_type: 'session_start',
|
|
121
|
+
}, options.cwd);
|
|
122
|
+
appendAuditEntry({ action: 'session_start', actor: actor.agent, actor_id: actor.agent_id, item_id: snapshot.session_id, item_type: 'session' }, options.cwd);
|
|
123
|
+
const agentGitHygiene = auditLocalAgentWorkspaceFiles(options.cwd ?? process.cwd());
|
|
124
|
+
return {
|
|
125
|
+
...snapshot,
|
|
126
|
+
...(agentGitHygiene.isGitRepo && (agentGitHygiene.missingGitignorePaths.length > 0 || agentGitHygiene.trackedPaths.length > 0)
|
|
127
|
+
? {
|
|
128
|
+
agent_git_hygiene: {
|
|
129
|
+
missing_gitignore_paths: agentGitHygiene.missingGitignorePaths,
|
|
130
|
+
tracked_paths: agentGitHygiene.trackedPaths,
|
|
131
|
+
},
|
|
132
|
+
}
|
|
133
|
+
: {}),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
export function loadSessionSnapshot(sessionId, cwd) {
|
|
137
|
+
const p = sessionSnapshotPath(sessionId, cwd);
|
|
138
|
+
if (!fs.existsSync(p))
|
|
139
|
+
return undefined;
|
|
140
|
+
try {
|
|
141
|
+
return SessionSnapshotSchema.parse(loadVersionedJsonFile('session_snapshot', p).document);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=session-start.js.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { memoryExists } from '../core/io.js';
|
|
2
|
+
import { hasElevatedAgent, requireMinimumTrustLevel, requireRegisteredAgentIdentity, setAgentTrustLevel, } from '../core/agent-registry.js';
|
|
3
|
+
import { appendAuditEntry } from '../core/audit.js';
|
|
4
|
+
import { buildOperationalIdentity } from '../core/identity.js';
|
|
5
|
+
import { resetCircuitBreaker } from '../core/circuit-breaker.js';
|
|
6
|
+
export function runSetTrust(agentName, options) {
|
|
7
|
+
if (!memoryExists(options.cwd)) {
|
|
8
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
// --reset-breaker path: only resets the circuit-breaker override, no trust change needed
|
|
12
|
+
if (options.resetBreaker) {
|
|
13
|
+
try {
|
|
14
|
+
resetCircuitBreaker(agentName, options.cwd);
|
|
15
|
+
if (options.json) {
|
|
16
|
+
console.log(JSON.stringify({ ok: true, agent: agentName, action: 'circuit_breaker_reset' }));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
console.log(`✔ Circuit-breaker reset for agent '${agentName}'. Auto-promote is restored.`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
24
|
+
console.error(`Error: ${msg}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (!options.level) {
|
|
30
|
+
console.error('Error: --level is required unless --reset-breaker is specified.');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
const level = options.level;
|
|
34
|
+
const validLevels = ['observer', 'contributor', 'trusted', 'curator'];
|
|
35
|
+
if (!validLevels.includes(level)) {
|
|
36
|
+
console.error(`Error: invalid trust level '${level}'. Must be one of: ${validLevels.join(', ')}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
let actor = 'unknown';
|
|
40
|
+
let actorId;
|
|
41
|
+
try {
|
|
42
|
+
const identity = buildOperationalIdentity();
|
|
43
|
+
actor = identity.agent;
|
|
44
|
+
actorId = identity.agent_id;
|
|
45
|
+
}
|
|
46
|
+
catch { /* use default */ }
|
|
47
|
+
let bootstrapCurator = false;
|
|
48
|
+
if (!hasElevatedAgent()) {
|
|
49
|
+
if (level !== 'curator') {
|
|
50
|
+
console.error("Error: no trusted or curator agent exists yet. Bootstrap the first curator with `brainclaw set-trust <agent> --level curator`.");
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
bootstrapCurator = true;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
try {
|
|
57
|
+
const actorIdentity = requireRegisteredAgentIdentity();
|
|
58
|
+
requireMinimumTrustLevel(actorIdentity, 'curator');
|
|
59
|
+
actor = actorIdentity.agent_name;
|
|
60
|
+
actorId = actorIdentity.agent_id;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
64
|
+
console.error(`Error: ${msg}`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
let updated;
|
|
69
|
+
try {
|
|
70
|
+
updated = setAgentTrustLevel(agentName, level);
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
74
|
+
console.error(`Error: ${msg}`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
appendAuditEntry({
|
|
78
|
+
action: 'trust_change',
|
|
79
|
+
actor,
|
|
80
|
+
actor_id: actorId,
|
|
81
|
+
item_id: updated.agent_id,
|
|
82
|
+
item_type: 'agent',
|
|
83
|
+
after: { trust_level: level },
|
|
84
|
+
reason: bootstrapCurator ? 'bootstrap_curator' : `set by ${actor}`,
|
|
85
|
+
});
|
|
86
|
+
if (options.json) {
|
|
87
|
+
console.log(JSON.stringify(updated, null, 2));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
console.log(`✔ Trust level for ${agentName} set to '${level}' (${updated.agent_id})`);
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=set-trust.js.map
|