hivemind-context-governance 1.3.0
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/CHANGELOG.md +95 -0
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/cli/init.d.ts +23 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +175 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +162 -0
- package/dist/cli.js.map +1 -0
- package/dist/dashboard/i18n/en.d.ts +43 -0
- package/dist/dashboard/i18n/en.d.ts.map +1 -0
- package/dist/dashboard/i18n/en.js +43 -0
- package/dist/dashboard/i18n/en.js.map +1 -0
- package/dist/dashboard/i18n/index.d.ts +10 -0
- package/dist/dashboard/i18n/index.d.ts.map +1 -0
- package/dist/dashboard/i18n/index.js +14 -0
- package/dist/dashboard/i18n/index.js.map +1 -0
- package/dist/dashboard/i18n/vi.d.ts +43 -0
- package/dist/dashboard/i18n/vi.d.ts.map +1 -0
- package/dist/dashboard/i18n/vi.js +43 -0
- package/dist/dashboard/i18n/vi.js.map +1 -0
- package/dist/dashboard/server.d.ts +23 -0
- package/dist/dashboard/server.d.ts.map +1 -0
- package/dist/dashboard/server.js +282 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/hooks/compaction.d.ts +21 -0
- package/dist/hooks/compaction.d.ts.map +1 -0
- package/dist/hooks/compaction.js +79 -0
- package/dist/hooks/compaction.js.map +1 -0
- package/dist/hooks/index.d.ts +8 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/session-lifecycle.d.ts +28 -0
- package/dist/hooks/session-lifecycle.d.ts.map +1 -0
- package/dist/hooks/session-lifecycle.js +94 -0
- package/dist/hooks/session-lifecycle.js.map +1 -0
- package/dist/hooks/tool-gate.d.ts +28 -0
- package/dist/hooks/tool-gate.d.ts.map +1 -0
- package/dist/hooks/tool-gate.js +152 -0
- package/dist/hooks/tool-gate.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +159 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/complexity.d.ts +41 -0
- package/dist/lib/complexity.d.ts.map +1 -0
- package/dist/lib/complexity.js +67 -0
- package/dist/lib/complexity.js.map +1 -0
- package/dist/lib/context-refresh.d.ts +46 -0
- package/dist/lib/context-refresh.d.ts.map +1 -0
- package/dist/lib/context-refresh.js +77 -0
- package/dist/lib/context-refresh.js.map +1 -0
- package/dist/lib/index.d.ts +10 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +10 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/logging.d.ts +13 -0
- package/dist/lib/logging.d.ts.map +1 -0
- package/dist/lib/logging.js +40 -0
- package/dist/lib/logging.js.map +1 -0
- package/dist/lib/persistence.d.ts +15 -0
- package/dist/lib/persistence.d.ts.map +1 -0
- package/dist/lib/persistence.js +62 -0
- package/dist/lib/persistence.js.map +1 -0
- package/dist/lib/planning-fs.d.ts +34 -0
- package/dist/lib/planning-fs.d.ts.map +1 -0
- package/dist/lib/planning-fs.js +134 -0
- package/dist/lib/planning-fs.js.map +1 -0
- package/dist/lib/sentiment.d.ts +31 -0
- package/dist/lib/sentiment.d.ts.map +1 -0
- package/dist/lib/sentiment.js +158 -0
- package/dist/lib/sentiment.js.map +1 -0
- package/dist/schemas/brain-state.d.ts +60 -0
- package/dist/schemas/brain-state.d.ts.map +1 -0
- package/dist/schemas/brain-state.js +176 -0
- package/dist/schemas/brain-state.js.map +1 -0
- package/dist/schemas/config.d.ts +51 -0
- package/dist/schemas/config.d.ts.map +1 -0
- package/dist/schemas/config.js +128 -0
- package/dist/schemas/config.js.map +1 -0
- package/dist/schemas/hierarchy.d.ts +22 -0
- package/dist/schemas/hierarchy.d.ts.map +1 -0
- package/dist/schemas/hierarchy.js +27 -0
- package/dist/schemas/hierarchy.js.map +1 -0
- package/dist/schemas/index.d.ts +7 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +7 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/tools/compact-session.d.ts +15 -0
- package/dist/tools/compact-session.d.ts.map +1 -0
- package/dist/tools/compact-session.js +76 -0
- package/dist/tools/compact-session.js.map +1 -0
- package/dist/tools/declare-intent.d.ts +15 -0
- package/dist/tools/declare-intent.d.ts.map +1 -0
- package/dist/tools/declare-intent.js +83 -0
- package/dist/tools/declare-intent.js.map +1 -0
- package/dist/tools/index.d.ts +14 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +14 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/map-context.d.ts +15 -0
- package/dist/tools/map-context.d.ts.map +1 -0
- package/dist/tools/map-context.js +85 -0
- package/dist/tools/map-context.js.map +1 -0
- package/dist/tools/self-rate.d.ts +15 -0
- package/dist/tools/self-rate.d.ts.map +1 -0
- package/dist/tools/self-rate.js +76 -0
- package/dist/tools/self-rate.js.map +1 -0
- package/package.json +71 -0
- package/src/cli/init.ts +214 -0
- package/src/cli.ts +178 -0
- package/src/dashboard/i18n/en.ts +43 -0
- package/src/dashboard/i18n/index.ts +20 -0
- package/src/dashboard/i18n/vi.ts +43 -0
- package/src/dashboard/server.ts +372 -0
- package/src/hooks/compaction.ts +104 -0
- package/src/hooks/index.ts +8 -0
- package/src/hooks/session-lifecycle.ts +133 -0
- package/src/hooks/tool-gate.ts +205 -0
- package/src/index.ts +206 -0
- package/src/lib/complexity.ts +96 -0
- package/src/lib/context-refresh.ts +107 -0
- package/src/lib/index.ts +10 -0
- package/src/lib/logging.ts +53 -0
- package/src/lib/persistence.ts +83 -0
- package/src/lib/planning-fs.ts +187 -0
- package/src/lib/sentiment.ts +183 -0
- package/src/schemas/brain-state.ts +257 -0
- package/src/schemas/config.ts +184 -0
- package/src/schemas/hierarchy.ts +58 -0
- package/src/schemas/index.ts +7 -0
- package/src/tools/compact-session.ts +97 -0
- package/src/tools/declare-intent.ts +104 -0
- package/src/tools/index.ts +14 -0
- package/src/tools/map-context.ts +105 -0
- package/src/tools/self-rate.ts +86 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* compact_session — Archive completed work and reset for next session.
|
|
3
|
+
*
|
|
4
|
+
* Agent Thought: "I'm done, archive this session"
|
|
5
|
+
*
|
|
6
|
+
* Design: Agent-Native lifecycle verb.
|
|
7
|
+
* 1. Iceberg — 0-1 args, system handles archive + reset + index update
|
|
8
|
+
* 2. Context Inference — reads active session from brain state
|
|
9
|
+
* 3. Signal-to-Noise — 1-line summary output
|
|
10
|
+
* 4. No-Shadowing — description matches agent intent
|
|
11
|
+
* 5. Native Parallelism — single call, atomic archival
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"
|
|
15
|
+
import { createStateManager, loadConfig } from "../lib/persistence.js"
|
|
16
|
+
import {
|
|
17
|
+
createBrainState,
|
|
18
|
+
generateSessionId,
|
|
19
|
+
lockSession,
|
|
20
|
+
} from "../schemas/brain-state.js"
|
|
21
|
+
import {
|
|
22
|
+
readActiveMd,
|
|
23
|
+
archiveSession,
|
|
24
|
+
updateIndexMd,
|
|
25
|
+
resetActiveMd,
|
|
26
|
+
listArchives,
|
|
27
|
+
} from "../lib/planning-fs.js"
|
|
28
|
+
|
|
29
|
+
export function createCompactSessionTool(directory: string): ToolDefinition {
|
|
30
|
+
return tool({
|
|
31
|
+
description:
|
|
32
|
+
"Archive completed work and reset for next session. " +
|
|
33
|
+
"Call this when you're done with your current work.",
|
|
34
|
+
args: {
|
|
35
|
+
summary: tool.schema
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Optional 1-line summary of what was accomplished"),
|
|
39
|
+
},
|
|
40
|
+
async execute(args) {
|
|
41
|
+
const stateManager = createStateManager(directory)
|
|
42
|
+
const config = await loadConfig(directory)
|
|
43
|
+
|
|
44
|
+
// Load brain state
|
|
45
|
+
const state = await stateManager.load()
|
|
46
|
+
if (!state) {
|
|
47
|
+
return "ERROR: No active session to compact."
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Read current active.md content for archival
|
|
51
|
+
const activeMd = await readActiveMd(directory)
|
|
52
|
+
const archiveContent = [
|
|
53
|
+
`# Archived Session: ${state.session.id}`,
|
|
54
|
+
"",
|
|
55
|
+
`**Mode**: ${state.session.mode}`,
|
|
56
|
+
`**Started**: ${new Date(state.session.start_time).toISOString()}`,
|
|
57
|
+
`**Archived**: ${new Date().toISOString()}`,
|
|
58
|
+
`**Turns**: ${state.metrics.turn_count}`,
|
|
59
|
+
`**Drift Score**: ${state.metrics.drift_score}/100`,
|
|
60
|
+
`**Files Touched**: ${state.metrics.files_touched.length}`,
|
|
61
|
+
`**Context Updates**: ${state.metrics.context_updates}`,
|
|
62
|
+
"",
|
|
63
|
+
"## Hierarchy at Archive",
|
|
64
|
+
state.hierarchy.trajectory ? `- **Trajectory**: ${state.hierarchy.trajectory}` : "",
|
|
65
|
+
state.hierarchy.tactic ? `- **Tactic**: ${state.hierarchy.tactic}` : "",
|
|
66
|
+
state.hierarchy.action ? `- **Action**: ${state.hierarchy.action}` : "",
|
|
67
|
+
"",
|
|
68
|
+
"## Session Content",
|
|
69
|
+
activeMd.body,
|
|
70
|
+
]
|
|
71
|
+
.filter(Boolean)
|
|
72
|
+
.join("\n")
|
|
73
|
+
|
|
74
|
+
// Archive the session
|
|
75
|
+
await archiveSession(directory, state.session.id, archiveContent)
|
|
76
|
+
|
|
77
|
+
// Update index.md with summary
|
|
78
|
+
const summaryLine =
|
|
79
|
+
args.summary ||
|
|
80
|
+
`Session ${state.session.id}: ${state.metrics.turn_count} turns, ${state.metrics.files_touched.length} files`
|
|
81
|
+
await updateIndexMd(directory, summaryLine)
|
|
82
|
+
|
|
83
|
+
// Reset active.md to template
|
|
84
|
+
await resetActiveMd(directory)
|
|
85
|
+
|
|
86
|
+
// Create fresh brain state (new session, locked)
|
|
87
|
+
const newSessionId = generateSessionId()
|
|
88
|
+
const newState = createBrainState(newSessionId, config)
|
|
89
|
+
await stateManager.save(lockSession(newState))
|
|
90
|
+
|
|
91
|
+
// Count archives for output
|
|
92
|
+
const archives = await listArchives(directory)
|
|
93
|
+
|
|
94
|
+
return `Archived. ${state.metrics.turn_count} turns, ${state.metrics.files_touched.length} files saved. ${archives.length} total archives. Session reset.`
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* declare_intent — Unlock session by declaring work mode and focus.
|
|
3
|
+
*
|
|
4
|
+
* Agent Thought: "I want to start working on something"
|
|
5
|
+
*
|
|
6
|
+
* Design: Agent-Native lifecycle verb.
|
|
7
|
+
* 1. Iceberg — 2 args, system handles state machine + file writes
|
|
8
|
+
* 2. Context Inference — session ID from context, governance mode from config
|
|
9
|
+
* 3. Signal-to-Noise — 1-line output
|
|
10
|
+
* 4. No-Shadowing — description matches agent intent
|
|
11
|
+
* 5. Native Parallelism — idempotent, safe to call repeatedly
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"
|
|
15
|
+
import { createStateManager, loadConfig } from "../lib/persistence.js"
|
|
16
|
+
import {
|
|
17
|
+
createBrainState,
|
|
18
|
+
generateSessionId,
|
|
19
|
+
unlockSession,
|
|
20
|
+
resetComplexityNudge,
|
|
21
|
+
} from "../schemas/brain-state.js"
|
|
22
|
+
import type { SessionMode } from "../schemas/brain-state.js"
|
|
23
|
+
import {
|
|
24
|
+
writeActiveMd,
|
|
25
|
+
readActiveMd,
|
|
26
|
+
initializePlanningDirectory,
|
|
27
|
+
} from "../lib/planning-fs.js"
|
|
28
|
+
|
|
29
|
+
const VALID_MODES: SessionMode[] = ["plan_driven", "quick_fix", "exploration"]
|
|
30
|
+
|
|
31
|
+
export function createDeclareIntentTool(directory: string): ToolDefinition {
|
|
32
|
+
return tool({
|
|
33
|
+
description:
|
|
34
|
+
"Unlock the session by declaring your work mode and focus. " +
|
|
35
|
+
"Call this before doing any work to set your intent.",
|
|
36
|
+
args: {
|
|
37
|
+
mode: tool.schema
|
|
38
|
+
.enum(VALID_MODES)
|
|
39
|
+
.describe("How are you approaching this work? plan_driven | quick_fix | exploration"),
|
|
40
|
+
focus: tool.schema
|
|
41
|
+
.string()
|
|
42
|
+
.describe("What are you working on? (1 sentence)"),
|
|
43
|
+
reason: tool.schema
|
|
44
|
+
.string()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("Why this mode? (optional context)"),
|
|
47
|
+
},
|
|
48
|
+
async execute(args) {
|
|
49
|
+
const config = await loadConfig(directory)
|
|
50
|
+
const stateManager = createStateManager(directory)
|
|
51
|
+
|
|
52
|
+
// Ensure planning directory exists
|
|
53
|
+
await initializePlanningDirectory(directory)
|
|
54
|
+
|
|
55
|
+
// Load or create brain state
|
|
56
|
+
let state = await stateManager.load()
|
|
57
|
+
if (!state) {
|
|
58
|
+
const sessionId = generateSessionId()
|
|
59
|
+
state = createBrainState(sessionId, config, args.mode)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Unlock session
|
|
63
|
+
state = unlockSession(state)
|
|
64
|
+
state.session.mode = args.mode
|
|
65
|
+
state.hierarchy.trajectory = args.focus
|
|
66
|
+
|
|
67
|
+
// Reset complexity nudge on new intent declaration
|
|
68
|
+
state = resetComplexityNudge(state)
|
|
69
|
+
|
|
70
|
+
// Save state
|
|
71
|
+
await stateManager.save(state)
|
|
72
|
+
|
|
73
|
+
// Update active.md with session info
|
|
74
|
+
const activeMd = await readActiveMd(directory)
|
|
75
|
+
activeMd.frontmatter = {
|
|
76
|
+
session_id: state.session.id,
|
|
77
|
+
mode: args.mode,
|
|
78
|
+
governance_status: "OPEN",
|
|
79
|
+
start_time: state.session.start_time,
|
|
80
|
+
last_updated: Date.now(),
|
|
81
|
+
}
|
|
82
|
+
activeMd.body = [
|
|
83
|
+
"# Active Session",
|
|
84
|
+
"",
|
|
85
|
+
"## Current Focus",
|
|
86
|
+
`**Mode**: ${args.mode}`,
|
|
87
|
+
`**Focus**: ${args.focus}`,
|
|
88
|
+
args.reason ? `**Reason**: ${args.reason}` : "",
|
|
89
|
+
"",
|
|
90
|
+
"## Completed",
|
|
91
|
+
"<!-- Items marked [x] get archived -->",
|
|
92
|
+
"",
|
|
93
|
+
"## Notes",
|
|
94
|
+
"<!-- Scratchpad - anything goes -->",
|
|
95
|
+
]
|
|
96
|
+
.filter(Boolean)
|
|
97
|
+
.join("\n")
|
|
98
|
+
|
|
99
|
+
await writeActiveMd(directory, activeMd)
|
|
100
|
+
|
|
101
|
+
return `Session: "${args.focus}". Mode: ${args.mode}. Status: OPEN.`
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool barrel exports — HiveMind lifecycle verbs
|
|
3
|
+
*
|
|
4
|
+
* 4 tools total:
|
|
5
|
+
* declare_intent — unlock session, set mode/focus
|
|
6
|
+
* map_context — update hierarchy level
|
|
7
|
+
* compact_session — archive + reset
|
|
8
|
+
* self_rate — agent self-assessment
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export { createDeclareIntentTool } from "./declare-intent.js"
|
|
12
|
+
export { createMapContextTool } from "./map-context.js"
|
|
13
|
+
export { createCompactSessionTool } from "./compact-session.js"
|
|
14
|
+
export { createSelfRateTool } from "./self-rate.js"
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* map_context — Update focus in the 3-level hierarchy.
|
|
3
|
+
*
|
|
4
|
+
* Agent Thought: "I need to update what I'm focused on"
|
|
5
|
+
*
|
|
6
|
+
* Design: Agent-Native lifecycle verb.
|
|
7
|
+
* 1. Iceberg — 2 args, system handles hierarchy state + file sync
|
|
8
|
+
* 2. Context Inference — reads current hierarchy from brain state
|
|
9
|
+
* 3. Signal-to-Noise — 1-line output with visual beacon
|
|
10
|
+
* 4. No-Shadowing — description matches agent intent
|
|
11
|
+
* 5. Native Parallelism — can update different levels independently
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"
|
|
15
|
+
import { createStateManager } from "../lib/persistence.js"
|
|
16
|
+
import {
|
|
17
|
+
resetTurnCount,
|
|
18
|
+
updateHierarchy,
|
|
19
|
+
} from "../schemas/brain-state.js"
|
|
20
|
+
import type { HierarchyLevel, ContextStatus } from "../schemas/hierarchy.js"
|
|
21
|
+
import {
|
|
22
|
+
readActiveMd,
|
|
23
|
+
writeActiveMd,
|
|
24
|
+
updateIndexMd,
|
|
25
|
+
} from "../lib/planning-fs.js"
|
|
26
|
+
|
|
27
|
+
const VALID_LEVELS: HierarchyLevel[] = ["trajectory", "tactic", "action"]
|
|
28
|
+
const VALID_STATUSES: ContextStatus[] = ["pending", "active", "complete", "blocked"]
|
|
29
|
+
|
|
30
|
+
export function createMapContextTool(directory: string): ToolDefinition {
|
|
31
|
+
return tool({
|
|
32
|
+
description:
|
|
33
|
+
"Update your current focus in the 3-level hierarchy. " +
|
|
34
|
+
"Call this when changing what you're working on.",
|
|
35
|
+
args: {
|
|
36
|
+
level: tool.schema
|
|
37
|
+
.enum(VALID_LEVELS)
|
|
38
|
+
.describe("Which level to update: trajectory | tactic | action"),
|
|
39
|
+
content: tool.schema
|
|
40
|
+
.string()
|
|
41
|
+
.describe("The new focus (1-2 sentences)"),
|
|
42
|
+
status: tool.schema
|
|
43
|
+
.enum(VALID_STATUSES)
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("Status of this context item (default: active)"),
|
|
46
|
+
},
|
|
47
|
+
async execute(args) {
|
|
48
|
+
const stateManager = createStateManager(directory)
|
|
49
|
+
const status = args.status ?? "active"
|
|
50
|
+
|
|
51
|
+
// Load brain state
|
|
52
|
+
let state = await stateManager.load()
|
|
53
|
+
if (!state) {
|
|
54
|
+
return "ERROR: No active session. Call declare_intent first."
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Update hierarchy
|
|
58
|
+
state = updateHierarchy(state, { [args.level]: args.content })
|
|
59
|
+
|
|
60
|
+
// Reset turn count on context update (re-engagement signal)
|
|
61
|
+
state = resetTurnCount(state)
|
|
62
|
+
|
|
63
|
+
// Save state
|
|
64
|
+
await stateManager.save(state)
|
|
65
|
+
|
|
66
|
+
// Sync to planning files
|
|
67
|
+
if (args.level === "trajectory") {
|
|
68
|
+
// Update index.md for trajectory-level changes
|
|
69
|
+
await updateIndexMd(directory, `[${status.toUpperCase()}] ${args.content}`)
|
|
70
|
+
} else {
|
|
71
|
+
// Update active.md for tactic/action level changes
|
|
72
|
+
const activeMd = await readActiveMd(directory)
|
|
73
|
+
const levelLabel = args.level === "tactic" ? "Tactic" : "Action"
|
|
74
|
+
const focusLine = `**${levelLabel}**: ${args.content} [${status.toUpperCase()}]`
|
|
75
|
+
|
|
76
|
+
// Append to current focus section
|
|
77
|
+
if (activeMd.body.includes("## Current Focus")) {
|
|
78
|
+
const parts = activeMd.body.split("## Current Focus")
|
|
79
|
+
const afterFocus = parts[1] || ""
|
|
80
|
+
const nextSection = afterFocus.indexOf("\n## ")
|
|
81
|
+
const focusContent =
|
|
82
|
+
nextSection > -1 ? afterFocus.substring(0, nextSection) : afterFocus
|
|
83
|
+
const rest =
|
|
84
|
+
nextSection > -1 ? afterFocus.substring(nextSection) : ""
|
|
85
|
+
|
|
86
|
+
activeMd.body =
|
|
87
|
+
parts[0] +
|
|
88
|
+
"## Current Focus" +
|
|
89
|
+
focusContent.trimEnd() +
|
|
90
|
+
"\n" +
|
|
91
|
+
focusLine +
|
|
92
|
+
"\n" +
|
|
93
|
+
rest
|
|
94
|
+
} else {
|
|
95
|
+
activeMd.body += `\n## Current Focus\n${focusLine}\n`
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
activeMd.frontmatter.last_updated = Date.now()
|
|
99
|
+
await writeActiveMd(directory, activeMd)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return `[${args.level}] "${args.content}" → ${status}`
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* self_rate — Agent self-assessment tool for drift detection.
|
|
3
|
+
*
|
|
4
|
+
* Agent Thought: "How well am I doing? Let me rate my performance."
|
|
5
|
+
*
|
|
6
|
+
* Design: Agent-Native lifecycle verb.
|
|
7
|
+
* 1. Iceberg — 1-3 args, system handles storage
|
|
8
|
+
* 2. Context Inference — turn number from state
|
|
9
|
+
* 3. Signal-to-Noise — 1-line output
|
|
10
|
+
* 4. No-Shadowing — description matches agent intent
|
|
11
|
+
* 5. Native Parallelism — safe to call multiple times
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { tool, type ToolDefinition } from "@opencode-ai/plugin/tool"
|
|
15
|
+
import { createStateManager } from "../lib/persistence.js"
|
|
16
|
+
import { addSelfRating } from "../schemas/brain-state.js"
|
|
17
|
+
import { createLogger } from "../lib/logging.js"
|
|
18
|
+
import { join } from "node:path"
|
|
19
|
+
|
|
20
|
+
export function createSelfRateTool(directory: string): ToolDefinition {
|
|
21
|
+
return tool({
|
|
22
|
+
description:
|
|
23
|
+
"Rate your own performance on the current task. " +
|
|
24
|
+
"Call this periodically to help detect drift and track progress.",
|
|
25
|
+
args: {
|
|
26
|
+
score: tool.schema
|
|
27
|
+
.number()
|
|
28
|
+
.min(1)
|
|
29
|
+
.max(10)
|
|
30
|
+
.describe("Your self-assessment score from 1-10 (10 = excellent)"),
|
|
31
|
+
reason: tool.schema
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("Why you gave this score (optional)"),
|
|
35
|
+
turn_context: tool.schema
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("What you're working on right now (optional)"),
|
|
39
|
+
},
|
|
40
|
+
async execute(args) {
|
|
41
|
+
const stateManager = createStateManager(directory)
|
|
42
|
+
const log = await createLogger(
|
|
43
|
+
join(directory, ".opencode", "planning", "logs"),
|
|
44
|
+
"self-rate"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
// Load current state
|
|
48
|
+
let state = await stateManager.load()
|
|
49
|
+
if (!state) {
|
|
50
|
+
return "ERROR: No active session. Call declare_intent first."
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Add the rating
|
|
54
|
+
state = addSelfRating(state, {
|
|
55
|
+
score: args.score,
|
|
56
|
+
reason: args.reason,
|
|
57
|
+
turn_context: args.turn_context,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Save state
|
|
61
|
+
await stateManager.save(state)
|
|
62
|
+
|
|
63
|
+
// Log to TUI
|
|
64
|
+
const turnNumber = state.metrics.turn_count
|
|
65
|
+
const logMessage = `[SelfRate] Turn ${turnNumber}: ${args.score}/10`
|
|
66
|
+
await log.info(logMessage)
|
|
67
|
+
|
|
68
|
+
// Build response
|
|
69
|
+
let response = `Rating recorded: ${args.score}/10 at turn ${turnNumber}`
|
|
70
|
+
if (args.reason) {
|
|
71
|
+
response += ` — "${args.reason}"`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Provide feedback based on score
|
|
75
|
+
if (args.score <= 3) {
|
|
76
|
+
response += "\n⚠️ Low score detected. Consider using compact_session to reset context."
|
|
77
|
+
} else if (args.score <= 7) {
|
|
78
|
+
response += "\n💡 Score suggests some drift. Consider map_context to refocus."
|
|
79
|
+
} else {
|
|
80
|
+
response += "\n✅ Good progress!"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return response
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
}
|