create-walle 0.9.11 → 0.9.13
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 +3 -3
- package/package.json +2 -2
- package/template/bin/dev.sh +7 -1
- package/template/bin/setup.js +53 -9
- package/template/bin/sync-images.js +53 -0
- package/template/builder-journal.md +17 -0
- package/template/claude-task-manager/api-prompts.js +98 -13
- package/template/claude-task-manager/api-reviews.js +82 -5
- package/template/claude-task-manager/db.js +32 -5
- package/template/claude-task-manager/docs/session-capture-foundation-design.md +1273 -0
- package/template/claude-task-manager/lib/claude-desktop-sessions.js +696 -0
- package/template/claude-task-manager/lib/coding-agent-models.js +49 -1
- package/template/claude-task-manager/lib/session-capture.js +421 -0
- package/template/claude-task-manager/lib/session-history.js +135 -15
- package/template/claude-task-manager/lib/session-jobs.js +10 -5
- package/template/claude-task-manager/lib/session-stream.js +87 -19
- package/template/claude-task-manager/lib/setup-provider-config.js +115 -0
- package/template/claude-task-manager/lib/walle-ctm-history.js +72 -0
- package/template/claude-task-manager/lib/walle-session-context.js +61 -0
- package/template/claude-task-manager/lib/walle-transcript.js +176 -0
- package/template/claude-task-manager/public/css/setup.css +35 -8
- package/template/claude-task-manager/public/css/walle-session.css +56 -0
- package/template/claude-task-manager/public/css/walle.css +120 -0
- package/template/claude-task-manager/public/index.html +814 -181
- package/template/claude-task-manager/public/js/message-renderer.js +148 -19
- package/template/claude-task-manager/public/js/reviews.js +120 -62
- package/template/claude-task-manager/public/js/setup.js +75 -31
- package/template/claude-task-manager/public/js/stream-view.js +115 -55
- package/template/claude-task-manager/public/js/walle-session.js +84 -2
- package/template/claude-task-manager/public/js/walle.js +308 -54
- package/template/claude-task-manager/server.js +1092 -146
- package/template/claude-task-manager/session-integrity.js +181 -54
- package/template/claude-task-manager/session-utils.js +123 -41
- package/template/claude-task-manager/workers/state-detectors/codex.js +5 -2
- package/template/package.json +1 -1
- package/template/wall-e/adapters/ctm.js +39 -18
- package/template/wall-e/agent-runners/contract.js +17 -0
- package/template/wall-e/agent-runners/index.js +22 -0
- package/template/wall-e/agent-runtime/harness.js +212 -0
- package/template/wall-e/agent-runtime/index.js +8 -0
- package/template/wall-e/agent-runtime/registry.js +67 -0
- package/template/wall-e/agent-runtime/session-store.js +179 -0
- package/template/wall-e/agent-runtime/spawn.js +208 -0
- package/template/wall-e/api-walle.js +174 -7
- package/template/wall-e/brain.js +266 -28
- package/template/wall-e/channels/policy.js +88 -0
- package/template/wall-e/channels/registry.js +15 -1
- package/template/wall-e/channels/reply-dispatcher.js +70 -0
- package/template/wall-e/channels/session-bindings.js +51 -0
- package/template/wall-e/chat/code-review-context.js +29 -0
- package/template/wall-e/chat.js +188 -42
- package/template/wall-e/coding/acp-adapter.js +188 -0
- package/template/wall-e/coding/agent-catalog.js +129 -0
- package/template/wall-e/coding/compaction-service.js +247 -0
- package/template/wall-e/coding/execution-trace.js +3 -0
- package/template/wall-e/coding/instruction-service.js +224 -0
- package/template/wall-e/coding/model-message.js +67 -0
- package/template/wall-e/coding/permission-rules-store.js +111 -0
- package/template/wall-e/coding/permission-service.js +266 -0
- package/template/wall-e/coding/prompt-bundle.js +67 -0
- package/template/wall-e/coding/prompt-runtime.js +243 -0
- package/template/wall-e/coding/provider-transform.js +188 -0
- package/template/wall-e/coding/runtime-mode.js +132 -0
- package/template/wall-e/coding/snapshot-service.js +155 -0
- package/template/wall-e/coding/stream-processor.js +268 -0
- package/template/wall-e/coding/task-tool.js +255 -0
- package/template/wall-e/coding/tool-registry.js +361 -0
- package/template/wall-e/coding/transcript-writer.js +143 -0
- package/template/wall-e/coding/workspace-replay.js +324 -0
- package/template/wall-e/coding-context.js +4 -22
- package/template/wall-e/coding-orchestrator.js +307 -18
- package/template/wall-e/coding-prompts.js +44 -3
- package/template/wall-e/context/context-builder.js +43 -1
- package/template/wall-e/context/topic-matcher.js +1 -1
- package/template/wall-e/eval/agent-runner.js +59 -13
- package/template/wall-e/eval/benchmarks/memory-retrieval.json +155 -57
- package/template/wall-e/eval/benchmarks.js +100 -16
- package/template/wall-e/eval/eval-orchestrator.js +218 -8
- package/template/wall-e/eval/harvester.js +62 -5
- package/template/wall-e/eval/head-to-head.js +23 -2
- package/template/wall-e/eval/humaneval-adapter.js +30 -5
- package/template/wall-e/eval/livecodebench-adapter.js +29 -5
- package/template/wall-e/eval/manifest.js +186 -0
- package/template/wall-e/eval/run-agent-benchmarks.js +66 -2
- package/template/wall-e/eval/session-retrieval-benchmark.js +150 -0
- package/template/wall-e/eval/session-transcripts.js +57 -4
- package/template/wall-e/eval/swebench-adapter.js +109 -3
- package/template/wall-e/evaluation/agent-router.js +53 -1
- package/template/wall-e/evaluation/coding-quorum.js +48 -1
- package/template/wall-e/evaluation/router.js +4 -2
- package/template/wall-e/evaluation/tier-selector.js +11 -1
- package/template/wall-e/extraction/contradiction.js +2 -2
- package/template/wall-e/extraction/indexer.js +2 -1
- package/template/wall-e/extraction/knowledge-extractor.js +2 -2
- package/template/wall-e/hooks/cli.js +92 -0
- package/template/wall-e/hooks/discovery.js +119 -0
- package/template/wall-e/hooks/index.js +7 -0
- package/template/wall-e/hooks/manifest.js +55 -0
- package/template/wall-e/hooks/runtime.js +84 -0
- package/template/wall-e/hooks/session-memory.js +225 -0
- package/template/wall-e/http/auth.js +6 -2
- package/template/wall-e/http/chat-api.js +54 -8
- package/template/wall-e/integrations/claude-plugin/hooks/hooks.json +27 -0
- package/template/wall-e/integrations/claude-plugin/hooks/walle-precompact-hook.sh +5 -0
- package/template/wall-e/integrations/claude-plugin/hooks/walle-stop-hook.sh +5 -0
- package/template/wall-e/integrations/codex-plugin/hooks/walle-hook.sh +7 -0
- package/template/wall-e/integrations/codex-plugin/hooks.json +37 -0
- package/template/wall-e/listening/calendar.js +3 -1
- package/template/wall-e/llm/client.js +64 -10
- package/template/wall-e/llm/google.js +39 -5
- package/template/wall-e/llm/ollama.js +1 -1
- package/template/wall-e/llm/ollama.plugin.json +1 -1
- package/template/wall-e/llm/provider-availability.js +10 -0
- package/template/wall-e/llm/provider-error.js +269 -0
- package/template/wall-e/llm/tool-adapter.js +48 -12
- package/template/wall-e/loops/boot.js +2 -1
- package/template/wall-e/loops/initiative.js +2 -2
- package/template/wall-e/loops/tasks.js +8 -47
- package/template/wall-e/loops/workspace-prompts.js +20 -0
- package/template/wall-e/mcp-server.js +442 -1
- package/template/wall-e/memory/session-ingest-service.js +159 -0
- package/template/wall-e/memory/source-indexer.js +289 -0
- package/template/wall-e/plugins/discovery.js +83 -0
- package/template/wall-e/plugins/manifest-loader.js +50 -10
- package/template/wall-e/plugins/manifest-schema.js +69 -0
- package/template/wall-e/plugins/model-catalog.js +55 -0
- package/template/wall-e/prompts/coding/base.txt +2 -0
- package/template/wall-e/prompts/coding/deepseek.txt +1 -0
- package/template/wall-e/prompts/coding/memory-protocol.md +9 -0
- package/template/wall-e/prompts/coding/plan.txt +1 -0
- package/template/wall-e/runtime/execution-trace.js +220 -0
- package/template/wall-e/security/audit.js +266 -0
- package/template/wall-e/security/ssrf.js +236 -0
- package/template/wall-e/session-files.js +303 -0
- package/template/wall-e/skills/_bundled/slack-backfill/SKILL.md +3 -0
- package/template/wall-e/skills/_bundled/slack-sync/SKILL.md +3 -0
- package/template/wall-e/skills/internal-skill-registry.js +2 -2
- package/template/wall-e/skills/script-skill-runner.js +143 -0
- package/template/wall-e/skills/skill-executor.js +5 -6
- package/template/wall-e/skills/skill-fallback.js +3 -1
- package/template/wall-e/skills/skill-harness-registry.js +7 -8
- package/template/wall-e/skills/skill-planner.js +52 -4
- package/template/wall-e/skills/slack-ingest.js +11 -3
- package/template/wall-e/sources/base.js +90 -0
- package/template/wall-e/sources/builtin.js +33 -0
- package/template/wall-e/sources/claude-code-jsonl.js +78 -0
- package/template/wall-e/sources/codex-jsonl.js +125 -0
- package/template/wall-e/sources/coding-session-utils.js +117 -0
- package/template/wall-e/sources/contract-suite.js +59 -0
- package/template/wall-e/sources/gemini-jsonl.js +85 -0
- package/template/wall-e/sources/index.js +9 -0
- package/template/wall-e/sources/jsonl-utils.js +181 -0
- package/template/wall-e/sources/record-types.js +252 -0
- package/template/wall-e/sources/registry.js +92 -0
- package/template/wall-e/sources/transforms.js +100 -0
- package/template/wall-e/sources/walle-jsonl.js +108 -0
- package/template/wall-e/tools/coding-middleware.js +31 -1
- package/template/wall-e/tools/file-tracker.js +25 -1
- package/template/wall-e/tools/local-tools.js +75 -47
- package/template/wall-e/tools/session-sharing.js +68 -1
- package/template/wall-e/tools/shell-analyzer.js +1 -1
- package/template/wall-e/tools/shell-policy.js +47 -0
- package/template/wall-e/tools/snapshot.js +42 -0
- package/template/wall-e/training/harvester.js +62 -5
- package/template/wall-e/utils/repair.js +253 -1
- package/template/website/index.html +3 -3
- package/template/wall-e/skills/_bundled/slack-mentions/.watched-threads.json +0 -18
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { spawn } = require('node:child_process');
|
|
6
|
+
const { findSkill } = require('./skill-loader');
|
|
7
|
+
|
|
8
|
+
const WALL_E_DIR = path.join(__dirname, '..');
|
|
9
|
+
|
|
10
|
+
async function runScriptSkillByName(skillName, task = {}, opts = {}) {
|
|
11
|
+
const skill = opts.skill || findSkill(skillName);
|
|
12
|
+
if (!skill) throw new Error(`Skill not found: ${skillName}`);
|
|
13
|
+
return runScriptSkill(skill, task, opts);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function runScriptSkill(skill, task = {}, opts = {}) {
|
|
17
|
+
if (!skill || skill.execution !== 'script') {
|
|
18
|
+
throw new Error(`Skill "${skill?.name || 'unknown'}" is not script-based`);
|
|
19
|
+
}
|
|
20
|
+
const entryPath = resolveSkillEntry(skill);
|
|
21
|
+
const skillConfig = buildSkillConfig(skill, task);
|
|
22
|
+
const args = buildSkillArgs(skill, skillConfig);
|
|
23
|
+
const log = typeof opts.log === 'function' ? opts.log : () => {};
|
|
24
|
+
log(`Running: node ${entryPath}${args.length ? ' ' + args.join(' ') : ''}`);
|
|
25
|
+
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const env = {
|
|
28
|
+
...process.env,
|
|
29
|
+
HOME: process.env.HOME,
|
|
30
|
+
WALL_E_SKILL_CONFIG: JSON.stringify(skillConfig),
|
|
31
|
+
WALL_E_SKILL_DIR: skill.dir,
|
|
32
|
+
WALL_E_SRC_DIR: WALL_E_DIR,
|
|
33
|
+
...(opts.env || {}),
|
|
34
|
+
};
|
|
35
|
+
if (task.checkpoint) env.WALL_E_CHECKPOINT = task.checkpoint;
|
|
36
|
+
|
|
37
|
+
const child = spawn(process.execPath, [entryPath, ...args], {
|
|
38
|
+
cwd: opts.cwd || WALL_E_DIR,
|
|
39
|
+
env,
|
|
40
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
41
|
+
});
|
|
42
|
+
if (typeof opts.onProcess === 'function') opts.onProcess(child);
|
|
43
|
+
|
|
44
|
+
let stdout = '';
|
|
45
|
+
let stderr = '';
|
|
46
|
+
child.stdout.setEncoding('utf8');
|
|
47
|
+
child.stderr.setEncoding('utf8');
|
|
48
|
+
child.stdout.on('data', (chunk) => {
|
|
49
|
+
stdout += chunk;
|
|
50
|
+
for (const line of chunk.split('\n').filter(Boolean)) {
|
|
51
|
+
log(line);
|
|
52
|
+
if (line.startsWith('CHECKPOINT:') && typeof opts.onCheckpoint === 'function') {
|
|
53
|
+
opts.onCheckpoint(line.slice(11).trim());
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
child.stderr.on('data', (chunk) => {
|
|
58
|
+
stderr += chunk;
|
|
59
|
+
for (const line of chunk.split('\n').filter(Boolean)) log(`[stderr] ${line}`);
|
|
60
|
+
});
|
|
61
|
+
child.on('close', (code) => {
|
|
62
|
+
if (typeof opts.onClose === 'function') opts.onClose(child, code);
|
|
63
|
+
if (code !== 0 && code !== null) {
|
|
64
|
+
reject(new Error(`Exit code ${code}: ${stderr.slice(0, 500)}`));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
let output = stdout.trim();
|
|
68
|
+
if (stderr.trim()) output += '\n[stderr] ' + stderr.trim();
|
|
69
|
+
resolve(output);
|
|
70
|
+
});
|
|
71
|
+
child.on('error', (err) => {
|
|
72
|
+
if (typeof opts.onClose === 'function') opts.onClose(child, null);
|
|
73
|
+
reject(err);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function resolveSkillEntry(skill) {
|
|
79
|
+
const entry = skill.entry || 'run.js';
|
|
80
|
+
const entryPath = path.resolve(skill.dir, entry);
|
|
81
|
+
const lexicalSkillRel = path.relative(path.resolve(skill.dir), entryPath);
|
|
82
|
+
const lexicalWallERel = path.relative(WALL_E_DIR, entryPath);
|
|
83
|
+
const lexicallyInsideSkill = !lexicalSkillRel.startsWith('..') && !path.isAbsolute(lexicalSkillRel);
|
|
84
|
+
const lexicallyInsideWallE = !lexicalWallERel.startsWith('..') && !path.isAbsolute(lexicalWallERel);
|
|
85
|
+
if (!lexicallyInsideSkill && !lexicallyInsideWallE) {
|
|
86
|
+
throw new Error(`Script skill ${skill.name} entry escapes skill and Wall-E directories`);
|
|
87
|
+
}
|
|
88
|
+
if (!fs.existsSync(entryPath)) {
|
|
89
|
+
throw new Error(`Skill entry point not found: ${entryPath}`);
|
|
90
|
+
}
|
|
91
|
+
const realEntryPath = fs.realpathSync(entryPath);
|
|
92
|
+
const realSkillDir = fs.realpathSync(skill.dir);
|
|
93
|
+
const realWallEDir = fs.realpathSync(WALL_E_DIR);
|
|
94
|
+
const skillRel = path.relative(realSkillDir, realEntryPath);
|
|
95
|
+
const wallERel = path.relative(realWallEDir, realEntryPath);
|
|
96
|
+
const insideSkillDir = !skillRel.startsWith('..') && !path.isAbsolute(skillRel);
|
|
97
|
+
const insideWallE = !wallERel.startsWith('..') && !path.isAbsolute(wallERel);
|
|
98
|
+
if (!insideSkillDir && !insideWallE) {
|
|
99
|
+
throw new Error(`Script skill ${skill.name} entry escapes skill and Wall-E directories`);
|
|
100
|
+
}
|
|
101
|
+
return realEntryPath;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function buildSkillConfig(skill, task = {}) {
|
|
105
|
+
const skillConfig = {};
|
|
106
|
+
if (skill.config) {
|
|
107
|
+
for (const [key, value] of Object.entries(skill.config)) {
|
|
108
|
+
if (value && typeof value === 'object' && value.default !== undefined) {
|
|
109
|
+
skillConfig[key] = value.default;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
let taskConfig = {};
|
|
114
|
+
if (task.skill_config) {
|
|
115
|
+
if (typeof task.skill_config === 'string') {
|
|
116
|
+
try {
|
|
117
|
+
taskConfig = JSON.parse(task.skill_config);
|
|
118
|
+
} catch {
|
|
119
|
+
taskConfig = {};
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
taskConfig = task.skill_config;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { ...skillConfig, ...taskConfig };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function buildSkillArgs(skill, skillConfig = {}) {
|
|
129
|
+
const args = [...(skill.args || [])].map(arg => String(arg));
|
|
130
|
+
if (skillConfig.mode && !args.includes(skillConfig.mode) && skill.name === 'slack-backfill') {
|
|
131
|
+
args.push(String(skillConfig.mode));
|
|
132
|
+
if (skillConfig.month) args.push(String(skillConfig.month));
|
|
133
|
+
}
|
|
134
|
+
return args;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = {
|
|
138
|
+
buildSkillArgs,
|
|
139
|
+
buildSkillConfig,
|
|
140
|
+
resolveSkillEntry,
|
|
141
|
+
runScriptSkill,
|
|
142
|
+
runScriptSkillByName,
|
|
143
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const brain = require('../brain');
|
|
3
|
-
const { getDefaultClient } = require('../llm/client');
|
|
3
|
+
const { getDefaultClient, getDefaultModel, resolveCompatibleModel } = require('../llm/client');
|
|
4
4
|
const { executeTool, getToolDefinitions } = require('./tool-executor');
|
|
5
5
|
const { augmentSystemPrompt } = require('../loops/loop-directives');
|
|
6
6
|
let telemetry;
|
|
@@ -16,7 +16,7 @@ try { telemetry = require('../telemetry'); } catch { telemetry = { track() {} };
|
|
|
16
16
|
*/
|
|
17
17
|
async function runSkill(skill, opts = {}) {
|
|
18
18
|
const { selectSkillHarness, ensureDefaultHarness } = require('./skill-harness-registry');
|
|
19
|
-
ensureDefaultHarness();
|
|
19
|
+
ensureDefaultHarness(_runSkillInternal);
|
|
20
20
|
const selection = selectSkillHarness(skill, opts);
|
|
21
21
|
// Log the selection — mirrors OpenClaw's logAgentHarnessSelection
|
|
22
22
|
// (selection.ts:246). Even at debug level, the trail exists in telemetry.
|
|
@@ -39,9 +39,6 @@ async function _runSkillInternal(skill, opts = {}) {
|
|
|
39
39
|
const ownerName = brain.getOwnerName();
|
|
40
40
|
const startTime = Date.now();
|
|
41
41
|
|
|
42
|
-
// Resolve model once so it can be tagged on skill_executions (Item G).
|
|
43
|
-
// Single source of truth — same precedence as the API call below.
|
|
44
|
-
const resolvedModel = opts.model || process.env.WALLE_MODEL || 'claude-haiku-4-5-20251001';
|
|
45
42
|
const decisionReason = opts.decision_reason || 'due';
|
|
46
43
|
|
|
47
44
|
// Build tool definitions — use skill's custom ones or defaults
|
|
@@ -52,8 +49,10 @@ async function _runSkillInternal(skill, opts = {}) {
|
|
|
52
49
|
// Build the prompt
|
|
53
50
|
const prompt = skill.prompt_template || buildDefaultPrompt(skill, ownerName);
|
|
54
51
|
|
|
55
|
-
// Call
|
|
52
|
+
// Call the configured provider with a provider-compatible model. This avoids
|
|
53
|
+
// sending stale Claude model ids to Gemini/OpenAI after provider switches.
|
|
56
54
|
const provider = getDefaultClient();
|
|
55
|
+
const resolvedModel = resolveCompatibleModel(opts.model || process.env.WALLE_MODEL || getDefaultModel(), provider.type);
|
|
57
56
|
const messages = [{ role: 'user', content: prompt }];
|
|
58
57
|
const allToolCalls = [];
|
|
59
58
|
const allToolResults = [];
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
let telemetry;
|
|
32
32
|
try { telemetry = require('../telemetry'); } catch { telemetry = { track() {} }; }
|
|
33
|
+
const { getDefaultModel, getDefaultProviderType, resolveCompatibleModel } = require('../llm/client');
|
|
33
34
|
|
|
34
35
|
function _resolveCandidates(skill, opts) {
|
|
35
36
|
if (Array.isArray(opts?.model_candidates) && opts.model_candidates.length) {
|
|
@@ -68,7 +69,8 @@ function _resolveCandidates(skill, opts) {
|
|
|
68
69
|
} catch {}
|
|
69
70
|
|
|
70
71
|
// Single-model fallback (no chain)
|
|
71
|
-
const
|
|
72
|
+
const providerType = opts?.provider || opts?.providerType || getDefaultProviderType();
|
|
73
|
+
const single = resolveCompatibleModel(opts?.model || process.env.WALLE_MODEL || getDefaultModel(), providerType);
|
|
72
74
|
return [single];
|
|
73
75
|
}
|
|
74
76
|
|
|
@@ -140,26 +140,25 @@ function listRegisteredSkillHarnesses() {
|
|
|
140
140
|
|
|
141
141
|
function clearSkillHarnessRegistry() {
|
|
142
142
|
_harnesses.clear();
|
|
143
|
+
_defaultHarnessRegistered = false;
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
// ── Bundled default harness: claude-tool-use ──
|
|
146
147
|
// The existing runSkill body becomes the universal-fallback harness.
|
|
147
148
|
// priority=0 ensures any registered specialized harness can preempt it.
|
|
148
|
-
//
|
|
149
|
-
// Lazy-required to avoid circular dep (skill-executor → registry → executor).
|
|
149
|
+
// The executor passes its implementation in so this registry stays dependency-light.
|
|
150
150
|
let _defaultHarnessRegistered = false;
|
|
151
|
-
function ensureDefaultHarness() {
|
|
151
|
+
function ensureDefaultHarness(runAttempt) {
|
|
152
152
|
if (_defaultHarnessRegistered) return;
|
|
153
|
+
if (typeof runAttempt !== 'function') {
|
|
154
|
+
throw new Error('ensureDefaultHarness requires a runAttempt function');
|
|
155
|
+
}
|
|
153
156
|
_defaultHarnessRegistered = true;
|
|
154
157
|
registerSkillHarness({
|
|
155
158
|
id: 'claude-tool-use',
|
|
156
159
|
label: 'Claude tool-use loop (default)',
|
|
157
160
|
supports: () => ({ supported: true, priority: 0 }),
|
|
158
|
-
runAttempt
|
|
159
|
-
// Lazy require avoids circular dependency at module load
|
|
160
|
-
const { _runSkillInternal } = require('./skill-executor');
|
|
161
|
-
return await _runSkillInternal(skill, opts);
|
|
162
|
-
},
|
|
161
|
+
runAttempt,
|
|
163
162
|
});
|
|
164
163
|
}
|
|
165
164
|
|
|
@@ -9,6 +9,41 @@ const { resolveInternalSkill } = require('./internal-skill-registry');
|
|
|
9
9
|
let telemetry;
|
|
10
10
|
try { telemetry = require('../telemetry'); } catch { telemetry = { trackError() {}, track() {} }; }
|
|
11
11
|
|
|
12
|
+
const DECISION_TELEMETRY_MIN_INTERVAL_MS = 15 * 60 * 1000;
|
|
13
|
+
const _lastDecisionTelemetry = new Map();
|
|
14
|
+
let _slackAlertMigrationDone = false;
|
|
15
|
+
|
|
16
|
+
function isBrainReady() {
|
|
17
|
+
try {
|
|
18
|
+
brain.getDb();
|
|
19
|
+
return true;
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function ensurePlannerBrainReady() {
|
|
26
|
+
if (isBrainReady()) return true;
|
|
27
|
+
try {
|
|
28
|
+
brain.initDb();
|
|
29
|
+
return true;
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.warn(`[skill-planner] Brain DB unavailable; skipping scheduled skills: ${err.message}`);
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function shouldTrackDecisionTelemetry(skillName, decision) {
|
|
37
|
+
if (!decision) return false;
|
|
38
|
+
if (decision.decision === 'run' || decision.reason === 'just_auto_disabled') return true;
|
|
39
|
+
const key = `${skillName}:${decision.decision}:${decision.reason || ''}`;
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
const last = _lastDecisionTelemetry.get(key) || 0;
|
|
42
|
+
if (now - last < DECISION_TELEMETRY_MIN_INTERVAL_MS) return false;
|
|
43
|
+
_lastDecisionTelemetry.set(key, now);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
12
47
|
// ── Service alerts ─────────────────────────────────────────────────────────
|
|
13
48
|
// Stored in kv_store as JSON array. Surfaces auth failures, version updates, etc.
|
|
14
49
|
|
|
@@ -41,6 +76,7 @@ function addServiceAlert(alert) {
|
|
|
41
76
|
const existing = alerts.findIndex(a => a.service === alert.service && a.type === alert.type);
|
|
42
77
|
if (existing >= 0) alerts.splice(existing, 1);
|
|
43
78
|
alerts.push({
|
|
79
|
+
...alert,
|
|
44
80
|
id: `${alert.service}:${alert.type}:${Date.now()}`,
|
|
45
81
|
service: alert.service,
|
|
46
82
|
type: alert.type,
|
|
@@ -99,9 +135,16 @@ function migrateSlackFamilyAlerts() {
|
|
|
99
135
|
}
|
|
100
136
|
}
|
|
101
137
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
migrateSlackFamilyAlerts();
|
|
138
|
+
function runSlackAlertMigrationIfReady() {
|
|
139
|
+
if (_slackAlertMigrationDone || !isBrainReady()) return;
|
|
140
|
+
migrateSlackFamilyAlerts();
|
|
141
|
+
_slackAlertMigrationDone = true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Run opportunistically at module load only after the brain singleton is ready.
|
|
145
|
+
// Some daemon paths import the planner before initDb(); touching KV there
|
|
146
|
+
// generated noisy "Database not initialized" telemetry.
|
|
147
|
+
runSlackAlertMigrationIfReady();
|
|
105
148
|
|
|
106
149
|
// Note: `isSkillBackedOff`, `isSkillDue`, and `getBackoffMs` were removed
|
|
107
150
|
// in favour of the pure-function `decideSkillDispatch` in
|
|
@@ -176,6 +219,9 @@ async function attemptAuthRecovery(skillName, err) {
|
|
|
176
219
|
* Run all enabled skills that are due.
|
|
177
220
|
*/
|
|
178
221
|
async function runDueSkills(opts = {}) {
|
|
222
|
+
if (!ensurePlannerBrainReady()) return { executed: 0, memoriesCreated: 0, skipped: 'db_unavailable' };
|
|
223
|
+
runSlackAlertMigrationIfReady();
|
|
224
|
+
|
|
179
225
|
// Iterate ALL skills — including disabled ones — so decideSkillDispatch
|
|
180
226
|
// emits per-skill decisions. The decision function itself filters
|
|
181
227
|
// disabled/auto_disabled/backed_off; we only proceed to dispatch when
|
|
@@ -211,7 +257,9 @@ async function runDueSkills(opts = {}) {
|
|
|
211
257
|
// Telemetry: structured event per decision per tick. Operator can
|
|
212
258
|
// grep "skill_dispatch_decision skill=X reason=backed_off" without
|
|
213
259
|
// reading source.
|
|
214
|
-
|
|
260
|
+
if (shouldTrackDecisionTelemetry(skill.name, decision)) {
|
|
261
|
+
try { telemetry.track('skill_dispatch_decision', { skill: skill.name, decision: decision.decision, reason: decision.reason }); } catch {}
|
|
262
|
+
}
|
|
215
263
|
// Recovery logger: long-form structured trace for post-incident
|
|
216
264
|
// debugging (Items E + A of error-recovery deep-dive).
|
|
217
265
|
if (decision.reason === 'backed_off') {
|
|
@@ -4,11 +4,13 @@ const path = require('path');
|
|
|
4
4
|
const brain = require('../brain');
|
|
5
5
|
|
|
6
6
|
const CLAUDE_DIR = path.join(process.env.HOME, '.claude');
|
|
7
|
-
const OWNER_SLACK_ID = process.env.SLACK_OWNER_USER_ID;
|
|
8
|
-
if (!OWNER_SLACK_ID) throw new Error('SLACK_OWNER_USER_ID env var required');
|
|
9
7
|
const BATCH_SIZE = 100; // messages per API call
|
|
10
8
|
const RATE_LIMIT_MS = 1200; // Slack rate limit: ~1 req/sec
|
|
11
9
|
|
|
10
|
+
function getOwnerSlackId() {
|
|
11
|
+
return process.env.SLACK_OWNER_USER_ID || null;
|
|
12
|
+
}
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
15
|
* Get fresh Slack token from Claude Code's credentials.
|
|
14
16
|
*/
|
|
@@ -181,7 +183,7 @@ async function fetchConversationHistory(conv, token, oldestTs) {
|
|
|
181
183
|
for (const msg of (data.messages || [])) {
|
|
182
184
|
if (!msg.text && !msg.attachments) continue;
|
|
183
185
|
|
|
184
|
-
const isOwner = msg.user ===
|
|
186
|
+
const isOwner = msg.user === getOwnerSlackId();
|
|
185
187
|
const text = msg.text || '';
|
|
186
188
|
if (!text.trim()) continue;
|
|
187
189
|
|
|
@@ -230,6 +232,12 @@ async function fetchConversationHistory(conv, token, oldestTs) {
|
|
|
230
232
|
* Returns { done, messagesIngested, conversationsProcessed }
|
|
231
233
|
*/
|
|
232
234
|
async function runIngestBatch(opts = {}) {
|
|
235
|
+
if (!getOwnerSlackId()) {
|
|
236
|
+
const message = 'SLACK_OWNER_USER_ID env var required';
|
|
237
|
+
console.log(`[slack-ingest] ${message}`);
|
|
238
|
+
return { done: false, messagesIngested: 0, conversationsProcessed: 0, error: 'missing_env', message };
|
|
239
|
+
}
|
|
240
|
+
|
|
233
241
|
// Pre-check: try to refresh token if it's about to expire
|
|
234
242
|
try {
|
|
235
243
|
const slackMcp = require('../tools/slack-mcp');
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
PRIVACY_CLASSES,
|
|
5
|
+
assertAdapterSchema,
|
|
6
|
+
assertIngestRecord,
|
|
7
|
+
assertSourceRef,
|
|
8
|
+
} = require('./record-types');
|
|
9
|
+
|
|
10
|
+
class SourceAdapterBase {
|
|
11
|
+
constructor(opts = {}) {
|
|
12
|
+
this.opts = opts;
|
|
13
|
+
this.schema = assertAdapterSchema(this.describeSchema());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describeSchema() {
|
|
17
|
+
return this.constructor.schema || {
|
|
18
|
+
adapterId: this.constructor.adapterId || this.constructor.name || 'source-adapter',
|
|
19
|
+
version: '0.0.0',
|
|
20
|
+
supportsIncremental: false,
|
|
21
|
+
defaultPrivacyClass: PRIVACY_CLASSES.PII_POTENTIAL,
|
|
22
|
+
declaredTransformations: [],
|
|
23
|
+
fields: {},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async *ingest() {
|
|
28
|
+
throw new Error(`${this.schema.adapterId}.ingest() not implemented`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async isCurrent() {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async sourceSummary() {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async close() {}
|
|
40
|
+
|
|
41
|
+
validateSource(source) {
|
|
42
|
+
return assertSourceRef({
|
|
43
|
+
privacyClass: this.schema.defaultPrivacyClass,
|
|
44
|
+
...source,
|
|
45
|
+
adapterId: source?.adapterId || this.schema.adapterId,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
validateRecord(record) {
|
|
50
|
+
return assertIngestRecord(record, this.schema);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
class SourceIngestContext {
|
|
55
|
+
constructor({ isCurrent = null, diagnostics = null } = {}) {
|
|
56
|
+
this._isCurrent = isCurrent;
|
|
57
|
+
this.diagnostics = diagnostics || [];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async isCurrent(item) {
|
|
61
|
+
if (typeof this._isCurrent !== 'function') return false;
|
|
62
|
+
return Boolean(await this._isCurrent(item));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
warn(message, fields = {}) {
|
|
66
|
+
this.diagnostics.push({ level: 'warn', message, ...fields });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
error(message, fields = {}) {
|
|
70
|
+
this.diagnostics.push({ level: 'error', message, ...fields });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function collectIngestRecords(adapter, source, { context = null, validate = true } = {}) {
|
|
75
|
+
const ctx = context || new SourceIngestContext();
|
|
76
|
+
const src = typeof adapter.validateSource === 'function' ? adapter.validateSource(source) : assertSourceRef(source);
|
|
77
|
+
const records = [];
|
|
78
|
+
for await (const record of adapter.ingest({ source: src, context: ctx })) {
|
|
79
|
+
records.push(validate && typeof adapter.validateRecord === 'function'
|
|
80
|
+
? adapter.validateRecord(record)
|
|
81
|
+
: record);
|
|
82
|
+
}
|
|
83
|
+
return { records, diagnostics: ctx.diagnostics, source: src };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
SourceAdapterBase,
|
|
88
|
+
SourceIngestContext,
|
|
89
|
+
collectIngestRecords,
|
|
90
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const registry = require('./registry');
|
|
4
|
+
const ClaudeCodeJsonlAdapter = require('./claude-code-jsonl');
|
|
5
|
+
const CodexJsonlAdapter = require('./codex-jsonl');
|
|
6
|
+
const GeminiJsonlAdapter = require('./gemini-jsonl');
|
|
7
|
+
const WalleJsonlAdapter = require('./walle-jsonl');
|
|
8
|
+
|
|
9
|
+
const BUILTIN_SOURCE_ADAPTERS = Object.freeze([
|
|
10
|
+
ClaudeCodeJsonlAdapter,
|
|
11
|
+
CodexJsonlAdapter,
|
|
12
|
+
GeminiJsonlAdapter,
|
|
13
|
+
WalleJsonlAdapter,
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
function ensureBuiltinSourceAdapters() {
|
|
17
|
+
for (const Adapter of BUILTIN_SOURCE_ADAPTERS) {
|
|
18
|
+
const instance = new Adapter();
|
|
19
|
+
if (!registry.has(instance.schema.adapterId)) {
|
|
20
|
+
registry.register(Adapter);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return registry;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
BUILTIN_SOURCE_ADAPTERS,
|
|
28
|
+
ClaudeCodeJsonlAdapter,
|
|
29
|
+
CodexJsonlAdapter,
|
|
30
|
+
GeminiJsonlAdapter,
|
|
31
|
+
WalleJsonlAdapter,
|
|
32
|
+
ensureBuiltinSourceAdapters,
|
|
33
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { SourceAdapterBase } = require('./base');
|
|
4
|
+
const {
|
|
5
|
+
cleanUserText,
|
|
6
|
+
extractMessageText,
|
|
7
|
+
extractToolCalls,
|
|
8
|
+
fileVersion,
|
|
9
|
+
looksLikeToolResult,
|
|
10
|
+
readJsonlFile,
|
|
11
|
+
sourceIdFromFile,
|
|
12
|
+
toIso,
|
|
13
|
+
} = require('./jsonl-utils');
|
|
14
|
+
const { buildExchangeRecords, messageToRecords } = require('./coding-session-utils');
|
|
15
|
+
|
|
16
|
+
class ClaudeCodeJsonlAdapter extends SourceAdapterBase {
|
|
17
|
+
static schema = {
|
|
18
|
+
adapterId: 'claude-code-jsonl',
|
|
19
|
+
version: '1.0.0',
|
|
20
|
+
supportsIncremental: true,
|
|
21
|
+
defaultPrivacyClass: 'pii_potential',
|
|
22
|
+
declaredTransformations: [
|
|
23
|
+
'newline_normalize',
|
|
24
|
+
'strip_tool_chrome',
|
|
25
|
+
'base64_payload_omit',
|
|
26
|
+
'tool_result_summarize',
|
|
27
|
+
'whitespace_trim',
|
|
28
|
+
'exchange_pair_chunk',
|
|
29
|
+
],
|
|
30
|
+
fields: {
|
|
31
|
+
sourceId: { type: 'string', required: true },
|
|
32
|
+
sourceFile: { type: 'string', required: true },
|
|
33
|
+
cwd: { type: 'string', required: false },
|
|
34
|
+
gitBranch: { type: 'string', required: false },
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
validateSource(source) {
|
|
39
|
+
const normalized = super.validateSource(source);
|
|
40
|
+
if (!source.sourceId) normalized.sourceId = sourceIdFromFile('claude', normalized.sourceFile || normalized.uri);
|
|
41
|
+
return normalized;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async *ingest({ source, context }) {
|
|
45
|
+
const filePath = source.sourceFile || source.uri;
|
|
46
|
+
const version = fileVersion(filePath);
|
|
47
|
+
const { events, diagnostics } = readJsonlFile(filePath);
|
|
48
|
+
for (const diagnostic of diagnostics) context?.warn?.(diagnostic.message, diagnostic);
|
|
49
|
+
|
|
50
|
+
const messages = [];
|
|
51
|
+
for (const { line, event } of events) {
|
|
52
|
+
if (!event?.message || !['user', 'assistant'].includes(event.type)) continue;
|
|
53
|
+
const role = event.message.role || event.type;
|
|
54
|
+
if (!['user', 'assistant'].includes(role)) continue;
|
|
55
|
+
let content = extractMessageText(event.message);
|
|
56
|
+
if (role === 'user') content = cleanUserText(content);
|
|
57
|
+
if (!content || looksLikeToolResult(content)) continue;
|
|
58
|
+
|
|
59
|
+
const message = {
|
|
60
|
+
itemId: event.uuid || `line-${line}`,
|
|
61
|
+
role,
|
|
62
|
+
content,
|
|
63
|
+
timestamp: toIso(event.timestamp || event.message.timestamp),
|
|
64
|
+
cwd: event.cwd || source.cwd || '',
|
|
65
|
+
gitBranch: event.gitBranch || source.metadata?.gitBranch || '',
|
|
66
|
+
rawType: event.type,
|
|
67
|
+
harness: 'claude-code',
|
|
68
|
+
toolCalls: extractToolCalls(event.message),
|
|
69
|
+
};
|
|
70
|
+
messages.push(message);
|
|
71
|
+
for (const record of messageToRecords({ source, message, version })) yield record;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (const record of buildExchangeRecords(source, messages)) yield record;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = ClaudeCodeJsonlAdapter;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { SourceAdapterBase } = require('./base');
|
|
4
|
+
const {
|
|
5
|
+
cleanUserText,
|
|
6
|
+
extractMessageText,
|
|
7
|
+
fileVersion,
|
|
8
|
+
looksLikeToolResult,
|
|
9
|
+
parseToolInput,
|
|
10
|
+
readJsonlFile,
|
|
11
|
+
sourceIdFromFile,
|
|
12
|
+
summarizeToolInput,
|
|
13
|
+
toIso,
|
|
14
|
+
} = require('./jsonl-utils');
|
|
15
|
+
const { buildExchangeRecords, memoryRecord, messageToRecords, sourceItem } = require('./coding-session-utils');
|
|
16
|
+
|
|
17
|
+
class CodexJsonlAdapter extends SourceAdapterBase {
|
|
18
|
+
static schema = {
|
|
19
|
+
adapterId: 'codex-jsonl',
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
supportsIncremental: true,
|
|
22
|
+
defaultPrivacyClass: 'pii_potential',
|
|
23
|
+
declaredTransformations: [
|
|
24
|
+
'newline_normalize',
|
|
25
|
+
'strip_tool_chrome',
|
|
26
|
+
'base64_payload_omit',
|
|
27
|
+
'tool_result_summarize',
|
|
28
|
+
'whitespace_trim',
|
|
29
|
+
'exchange_pair_chunk',
|
|
30
|
+
],
|
|
31
|
+
fields: {
|
|
32
|
+
sourceId: { type: 'string', required: true },
|
|
33
|
+
sourceFile: { type: 'string', required: true },
|
|
34
|
+
cwd: { type: 'string', required: false },
|
|
35
|
+
gitBranch: { type: 'string', required: false },
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
validateSource(source) {
|
|
40
|
+
const normalized = super.validateSource(source);
|
|
41
|
+
if (!source.sourceId) normalized.sourceId = sourceIdFromFile('codex', normalized.sourceFile || normalized.uri);
|
|
42
|
+
return normalized;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async *ingest({ source, context }) {
|
|
46
|
+
const filePath = source.sourceFile || source.uri;
|
|
47
|
+
const version = fileVersion(filePath);
|
|
48
|
+
const { events, diagnostics } = readJsonlFile(filePath);
|
|
49
|
+
for (const diagnostic of diagnostics) context?.warn?.(diagnostic.message, diagnostic);
|
|
50
|
+
|
|
51
|
+
let cwd = source.cwd || '';
|
|
52
|
+
let gitBranch = source.metadata?.gitBranch || '';
|
|
53
|
+
const messages = [];
|
|
54
|
+
const seen = new Set();
|
|
55
|
+
|
|
56
|
+
for (const { line, event } of events) {
|
|
57
|
+
const payload = event.payload || {};
|
|
58
|
+
const item = payload.item || payload;
|
|
59
|
+
if (event.type === 'session_meta') {
|
|
60
|
+
cwd = payload.cwd || cwd;
|
|
61
|
+
gitBranch = payload.gitBranch || gitBranch;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (event.type === 'turn_context') {
|
|
65
|
+
cwd = payload.cwd || cwd;
|
|
66
|
+
gitBranch = payload.gitBranch || gitBranch;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (event.type !== 'response_item' && event.type !== 'event_msg') continue;
|
|
71
|
+
|
|
72
|
+
const role = item.role || payload.role || event.role || null;
|
|
73
|
+
if (role === 'user' || role === 'assistant') {
|
|
74
|
+
const content = role === 'user'
|
|
75
|
+
? cleanUserText(extractMessageText(item))
|
|
76
|
+
: extractMessageText(item);
|
|
77
|
+
if (!content || looksLikeToolResult(content)) continue;
|
|
78
|
+
const itemId = item.id || payload.id || `${event.type}-${line}`;
|
|
79
|
+
const key = `${role}:${itemId}:${content}`;
|
|
80
|
+
if (seen.has(key)) continue;
|
|
81
|
+
seen.add(key);
|
|
82
|
+
const message = {
|
|
83
|
+
itemId,
|
|
84
|
+
role,
|
|
85
|
+
content,
|
|
86
|
+
timestamp: toIso(event.timestamp || payload.timestamp || item.timestamp),
|
|
87
|
+
cwd,
|
|
88
|
+
gitBranch,
|
|
89
|
+
rawType: event.type,
|
|
90
|
+
harness: 'codex',
|
|
91
|
+
toolCalls: [],
|
|
92
|
+
};
|
|
93
|
+
messages.push(message);
|
|
94
|
+
for (const record of messageToRecords({ source, message, version })) yield record;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (payload.type === 'function_call' || item.type === 'function_call') {
|
|
99
|
+
const name = item.name || payload.name || 'tool';
|
|
100
|
+
const input = parseToolInput(item.arguments ?? payload.arguments ?? item.input ?? payload.input);
|
|
101
|
+
const itemId = item.id || payload.id || `tool-${line}`;
|
|
102
|
+
yield sourceItem({
|
|
103
|
+
source,
|
|
104
|
+
itemId,
|
|
105
|
+
version,
|
|
106
|
+
timestamp: event.timestamp || payload.timestamp,
|
|
107
|
+
metadata: { role: 'tool', cwd, gitBranch, name, input },
|
|
108
|
+
});
|
|
109
|
+
yield memoryRecord({
|
|
110
|
+
source,
|
|
111
|
+
itemId: `${itemId}:tool`,
|
|
112
|
+
memoryType: 'coding_session_tool_call',
|
|
113
|
+
role: 'tool',
|
|
114
|
+
contentRaw: `[Tool: ${name}] ${summarizeToolInput(name, input)}`,
|
|
115
|
+
timestamp: event.timestamp || payload.timestamp,
|
|
116
|
+
metadata: { cwd, gitBranch, name, input },
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const record of buildExchangeRecords(source, messages)) yield record;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = CodexJsonlAdapter;
|