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.
Files changed (167) hide show
  1. package/README.md +3 -3
  2. package/package.json +2 -2
  3. package/template/bin/dev.sh +7 -1
  4. package/template/bin/setup.js +53 -9
  5. package/template/bin/sync-images.js +53 -0
  6. package/template/builder-journal.md +17 -0
  7. package/template/claude-task-manager/api-prompts.js +98 -13
  8. package/template/claude-task-manager/api-reviews.js +82 -5
  9. package/template/claude-task-manager/db.js +32 -5
  10. package/template/claude-task-manager/docs/session-capture-foundation-design.md +1273 -0
  11. package/template/claude-task-manager/lib/claude-desktop-sessions.js +696 -0
  12. package/template/claude-task-manager/lib/coding-agent-models.js +49 -1
  13. package/template/claude-task-manager/lib/session-capture.js +421 -0
  14. package/template/claude-task-manager/lib/session-history.js +135 -15
  15. package/template/claude-task-manager/lib/session-jobs.js +10 -5
  16. package/template/claude-task-manager/lib/session-stream.js +87 -19
  17. package/template/claude-task-manager/lib/setup-provider-config.js +115 -0
  18. package/template/claude-task-manager/lib/walle-ctm-history.js +72 -0
  19. package/template/claude-task-manager/lib/walle-session-context.js +61 -0
  20. package/template/claude-task-manager/lib/walle-transcript.js +176 -0
  21. package/template/claude-task-manager/public/css/setup.css +35 -8
  22. package/template/claude-task-manager/public/css/walle-session.css +56 -0
  23. package/template/claude-task-manager/public/css/walle.css +120 -0
  24. package/template/claude-task-manager/public/index.html +814 -181
  25. package/template/claude-task-manager/public/js/message-renderer.js +148 -19
  26. package/template/claude-task-manager/public/js/reviews.js +120 -62
  27. package/template/claude-task-manager/public/js/setup.js +75 -31
  28. package/template/claude-task-manager/public/js/stream-view.js +115 -55
  29. package/template/claude-task-manager/public/js/walle-session.js +84 -2
  30. package/template/claude-task-manager/public/js/walle.js +308 -54
  31. package/template/claude-task-manager/server.js +1092 -146
  32. package/template/claude-task-manager/session-integrity.js +181 -54
  33. package/template/claude-task-manager/session-utils.js +123 -41
  34. package/template/claude-task-manager/workers/state-detectors/codex.js +5 -2
  35. package/template/package.json +1 -1
  36. package/template/wall-e/adapters/ctm.js +39 -18
  37. package/template/wall-e/agent-runners/contract.js +17 -0
  38. package/template/wall-e/agent-runners/index.js +22 -0
  39. package/template/wall-e/agent-runtime/harness.js +212 -0
  40. package/template/wall-e/agent-runtime/index.js +8 -0
  41. package/template/wall-e/agent-runtime/registry.js +67 -0
  42. package/template/wall-e/agent-runtime/session-store.js +179 -0
  43. package/template/wall-e/agent-runtime/spawn.js +208 -0
  44. package/template/wall-e/api-walle.js +174 -7
  45. package/template/wall-e/brain.js +266 -28
  46. package/template/wall-e/channels/policy.js +88 -0
  47. package/template/wall-e/channels/registry.js +15 -1
  48. package/template/wall-e/channels/reply-dispatcher.js +70 -0
  49. package/template/wall-e/channels/session-bindings.js +51 -0
  50. package/template/wall-e/chat/code-review-context.js +29 -0
  51. package/template/wall-e/chat.js +188 -42
  52. package/template/wall-e/coding/acp-adapter.js +188 -0
  53. package/template/wall-e/coding/agent-catalog.js +129 -0
  54. package/template/wall-e/coding/compaction-service.js +247 -0
  55. package/template/wall-e/coding/execution-trace.js +3 -0
  56. package/template/wall-e/coding/instruction-service.js +224 -0
  57. package/template/wall-e/coding/model-message.js +67 -0
  58. package/template/wall-e/coding/permission-rules-store.js +111 -0
  59. package/template/wall-e/coding/permission-service.js +266 -0
  60. package/template/wall-e/coding/prompt-bundle.js +67 -0
  61. package/template/wall-e/coding/prompt-runtime.js +243 -0
  62. package/template/wall-e/coding/provider-transform.js +188 -0
  63. package/template/wall-e/coding/runtime-mode.js +132 -0
  64. package/template/wall-e/coding/snapshot-service.js +155 -0
  65. package/template/wall-e/coding/stream-processor.js +268 -0
  66. package/template/wall-e/coding/task-tool.js +255 -0
  67. package/template/wall-e/coding/tool-registry.js +361 -0
  68. package/template/wall-e/coding/transcript-writer.js +143 -0
  69. package/template/wall-e/coding/workspace-replay.js +324 -0
  70. package/template/wall-e/coding-context.js +4 -22
  71. package/template/wall-e/coding-orchestrator.js +307 -18
  72. package/template/wall-e/coding-prompts.js +44 -3
  73. package/template/wall-e/context/context-builder.js +43 -1
  74. package/template/wall-e/context/topic-matcher.js +1 -1
  75. package/template/wall-e/eval/agent-runner.js +59 -13
  76. package/template/wall-e/eval/benchmarks/memory-retrieval.json +155 -57
  77. package/template/wall-e/eval/benchmarks.js +100 -16
  78. package/template/wall-e/eval/eval-orchestrator.js +218 -8
  79. package/template/wall-e/eval/harvester.js +62 -5
  80. package/template/wall-e/eval/head-to-head.js +23 -2
  81. package/template/wall-e/eval/humaneval-adapter.js +30 -5
  82. package/template/wall-e/eval/livecodebench-adapter.js +29 -5
  83. package/template/wall-e/eval/manifest.js +186 -0
  84. package/template/wall-e/eval/run-agent-benchmarks.js +66 -2
  85. package/template/wall-e/eval/session-retrieval-benchmark.js +150 -0
  86. package/template/wall-e/eval/session-transcripts.js +57 -4
  87. package/template/wall-e/eval/swebench-adapter.js +109 -3
  88. package/template/wall-e/evaluation/agent-router.js +53 -1
  89. package/template/wall-e/evaluation/coding-quorum.js +48 -1
  90. package/template/wall-e/evaluation/router.js +4 -2
  91. package/template/wall-e/evaluation/tier-selector.js +11 -1
  92. package/template/wall-e/extraction/contradiction.js +2 -2
  93. package/template/wall-e/extraction/indexer.js +2 -1
  94. package/template/wall-e/extraction/knowledge-extractor.js +2 -2
  95. package/template/wall-e/hooks/cli.js +92 -0
  96. package/template/wall-e/hooks/discovery.js +119 -0
  97. package/template/wall-e/hooks/index.js +7 -0
  98. package/template/wall-e/hooks/manifest.js +55 -0
  99. package/template/wall-e/hooks/runtime.js +84 -0
  100. package/template/wall-e/hooks/session-memory.js +225 -0
  101. package/template/wall-e/http/auth.js +6 -2
  102. package/template/wall-e/http/chat-api.js +54 -8
  103. package/template/wall-e/integrations/claude-plugin/hooks/hooks.json +27 -0
  104. package/template/wall-e/integrations/claude-plugin/hooks/walle-precompact-hook.sh +5 -0
  105. package/template/wall-e/integrations/claude-plugin/hooks/walle-stop-hook.sh +5 -0
  106. package/template/wall-e/integrations/codex-plugin/hooks/walle-hook.sh +7 -0
  107. package/template/wall-e/integrations/codex-plugin/hooks.json +37 -0
  108. package/template/wall-e/listening/calendar.js +3 -1
  109. package/template/wall-e/llm/client.js +64 -10
  110. package/template/wall-e/llm/google.js +39 -5
  111. package/template/wall-e/llm/ollama.js +1 -1
  112. package/template/wall-e/llm/ollama.plugin.json +1 -1
  113. package/template/wall-e/llm/provider-availability.js +10 -0
  114. package/template/wall-e/llm/provider-error.js +269 -0
  115. package/template/wall-e/llm/tool-adapter.js +48 -12
  116. package/template/wall-e/loops/boot.js +2 -1
  117. package/template/wall-e/loops/initiative.js +2 -2
  118. package/template/wall-e/loops/tasks.js +8 -47
  119. package/template/wall-e/loops/workspace-prompts.js +20 -0
  120. package/template/wall-e/mcp-server.js +442 -1
  121. package/template/wall-e/memory/session-ingest-service.js +159 -0
  122. package/template/wall-e/memory/source-indexer.js +289 -0
  123. package/template/wall-e/plugins/discovery.js +83 -0
  124. package/template/wall-e/plugins/manifest-loader.js +50 -10
  125. package/template/wall-e/plugins/manifest-schema.js +69 -0
  126. package/template/wall-e/plugins/model-catalog.js +55 -0
  127. package/template/wall-e/prompts/coding/base.txt +2 -0
  128. package/template/wall-e/prompts/coding/deepseek.txt +1 -0
  129. package/template/wall-e/prompts/coding/memory-protocol.md +9 -0
  130. package/template/wall-e/prompts/coding/plan.txt +1 -0
  131. package/template/wall-e/runtime/execution-trace.js +220 -0
  132. package/template/wall-e/security/audit.js +266 -0
  133. package/template/wall-e/security/ssrf.js +236 -0
  134. package/template/wall-e/session-files.js +303 -0
  135. package/template/wall-e/skills/_bundled/slack-backfill/SKILL.md +3 -0
  136. package/template/wall-e/skills/_bundled/slack-sync/SKILL.md +3 -0
  137. package/template/wall-e/skills/internal-skill-registry.js +2 -2
  138. package/template/wall-e/skills/script-skill-runner.js +143 -0
  139. package/template/wall-e/skills/skill-executor.js +5 -6
  140. package/template/wall-e/skills/skill-fallback.js +3 -1
  141. package/template/wall-e/skills/skill-harness-registry.js +7 -8
  142. package/template/wall-e/skills/skill-planner.js +52 -4
  143. package/template/wall-e/skills/slack-ingest.js +11 -3
  144. package/template/wall-e/sources/base.js +90 -0
  145. package/template/wall-e/sources/builtin.js +33 -0
  146. package/template/wall-e/sources/claude-code-jsonl.js +78 -0
  147. package/template/wall-e/sources/codex-jsonl.js +125 -0
  148. package/template/wall-e/sources/coding-session-utils.js +117 -0
  149. package/template/wall-e/sources/contract-suite.js +59 -0
  150. package/template/wall-e/sources/gemini-jsonl.js +85 -0
  151. package/template/wall-e/sources/index.js +9 -0
  152. package/template/wall-e/sources/jsonl-utils.js +181 -0
  153. package/template/wall-e/sources/record-types.js +252 -0
  154. package/template/wall-e/sources/registry.js +92 -0
  155. package/template/wall-e/sources/transforms.js +100 -0
  156. package/template/wall-e/sources/walle-jsonl.js +108 -0
  157. package/template/wall-e/tools/coding-middleware.js +31 -1
  158. package/template/wall-e/tools/file-tracker.js +25 -1
  159. package/template/wall-e/tools/local-tools.js +75 -47
  160. package/template/wall-e/tools/session-sharing.js +68 -1
  161. package/template/wall-e/tools/shell-analyzer.js +1 -1
  162. package/template/wall-e/tools/shell-policy.js +47 -0
  163. package/template/wall-e/tools/snapshot.js +42 -0
  164. package/template/wall-e/training/harvester.js +62 -5
  165. package/template/wall-e/utils/repair.js +253 -1
  166. package/template/website/index.html +3 -3
  167. package/template/wall-e/skills/_bundled/slack-mentions/.watched-threads.json +0 -18
@@ -0,0 +1,117 @@
1
+ 'use strict';
2
+
3
+ const { INGEST_MODES, PRIVACY_CLASSES, RECORD_TYPES } = require('./record-types');
4
+ const { cleanedContent, toIso } = require('./jsonl-utils');
5
+
6
+ const DEFAULT_TEXT_TRANSFORMS = Object.freeze([
7
+ 'newline_normalize',
8
+ 'strip_tool_chrome',
9
+ 'base64_payload_omit',
10
+ 'tool_result_summarize',
11
+ 'whitespace_trim',
12
+ ]);
13
+
14
+ function sourceItem({ source, itemId, version, timestamp, ingestMode, privacyClass, metadata }) {
15
+ return {
16
+ type: RECORD_TYPES.SOURCE_ITEM,
17
+ sourceId: source.sourceId,
18
+ sourceFile: source.sourceFile,
19
+ itemId,
20
+ version,
21
+ timestamp: toIso(timestamp),
22
+ ingestMode: ingestMode || INGEST_MODES.WHOLE_RECORD,
23
+ privacyClass: privacyClass || source.privacyClass || PRIVACY_CLASSES.PII_POTENTIAL,
24
+ metadata: metadata || {},
25
+ };
26
+ }
27
+
28
+ function memoryRecord({
29
+ source,
30
+ itemId,
31
+ memoryType,
32
+ role,
33
+ contentRaw,
34
+ transforms = DEFAULT_TEXT_TRANSFORMS,
35
+ timestamp,
36
+ privacyClass,
37
+ metadata,
38
+ }) {
39
+ return {
40
+ type: RECORD_TYPES.MEMORY_RECORD,
41
+ sourceId: source.sourceId,
42
+ sourceFile: source.sourceFile,
43
+ itemId,
44
+ role,
45
+ memoryType,
46
+ contentRaw,
47
+ content: cleanedContent(contentRaw, transforms),
48
+ transforms,
49
+ timestamp: toIso(timestamp),
50
+ ingestMode: INGEST_MODES.WHOLE_RECORD,
51
+ privacyClass: privacyClass || source.privacyClass || PRIVACY_CLASSES.PII_POTENTIAL,
52
+ metadata: metadata || {},
53
+ };
54
+ }
55
+
56
+ function messageToRecords({ source, message, version }) {
57
+ const metadata = {
58
+ role: message.role,
59
+ cwd: message.cwd || '',
60
+ gitBranch: message.gitBranch || '',
61
+ toolCalls: message.toolCalls || [],
62
+ rawType: message.rawType || '',
63
+ harness: message.harness || '',
64
+ };
65
+ const item = sourceItem({
66
+ source,
67
+ itemId: message.itemId,
68
+ version,
69
+ timestamp: message.timestamp,
70
+ metadata,
71
+ });
72
+ const memory = memoryRecord({
73
+ source,
74
+ itemId: `${message.itemId}:message`,
75
+ memoryType: message.memoryType || `coding_session_${message.role}_message`,
76
+ role: message.role,
77
+ contentRaw: message.content,
78
+ timestamp: message.timestamp,
79
+ metadata,
80
+ });
81
+ return [item, memory];
82
+ }
83
+
84
+ function buildExchangeRecords(source, messages) {
85
+ const records = [];
86
+ for (let i = 0; i < messages.length - 1; i++) {
87
+ const user = messages[i];
88
+ const assistant = messages[i + 1];
89
+ if (user.role !== 'user' || assistant.role !== 'assistant') continue;
90
+ const contentRaw = `Q: ${user.content}\nA: ${assistant.content}`;
91
+ records.push(memoryRecord({
92
+ source,
93
+ itemId: `exchange:${user.itemId}:${assistant.itemId}`,
94
+ memoryType: 'coding_session_exchange',
95
+ role: 'exchange',
96
+ contentRaw,
97
+ transforms: [...DEFAULT_TEXT_TRANSFORMS, 'exchange_pair_chunk'],
98
+ timestamp: user.timestamp || assistant.timestamp,
99
+ metadata: {
100
+ userItemId: user.itemId,
101
+ assistantItemId: assistant.itemId,
102
+ cwd: user.cwd || assistant.cwd || '',
103
+ gitBranch: user.gitBranch || assistant.gitBranch || '',
104
+ toolCalls: assistant.toolCalls || [],
105
+ },
106
+ }));
107
+ }
108
+ return records;
109
+ }
110
+
111
+ module.exports = {
112
+ DEFAULT_TEXT_TRANSFORMS,
113
+ buildExchangeRecords,
114
+ memoryRecord,
115
+ messageToRecords,
116
+ sourceItem,
117
+ };
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+
3
+ const assert = require('node:assert/strict');
4
+ const { collectIngestRecords } = require('./base');
5
+ const {
6
+ RECORD_TYPES,
7
+ SourceValidationError,
8
+ validateAdapterSchema,
9
+ validateIngestRecord,
10
+ validateSourceRef,
11
+ } = require('./record-types');
12
+
13
+ async function assertSourceAdapterContract(adapter, { source, minRecords = 1, requireMemoryRecord = true } = {}) {
14
+ const schemaValidation = validateAdapterSchema(adapter.describeSchema ? adapter.describeSchema() : adapter.schema);
15
+ assert.equal(schemaValidation.ok, true, schemaValidation.errors.join('; '));
16
+
17
+ const sourceValidation = validateSourceRef(source);
18
+ assert.equal(sourceValidation.ok, true, sourceValidation.errors.join('; '));
19
+
20
+ const { records } = await collectIngestRecords(adapter, sourceValidation.value, { validate: false });
21
+ assert.ok(records.length >= minRecords, `expected at least ${minRecords} source records`);
22
+
23
+ let memoryRecords = 0;
24
+ for (const record of records) {
25
+ const validation = validateIngestRecord(record, schemaValidation.value);
26
+ assert.equal(validation.ok, true, validation.errors.join('; '));
27
+ if (record.type === RECORD_TYPES.MEMORY_RECORD) memoryRecords += 1;
28
+ }
29
+
30
+ if (requireMemoryRecord) {
31
+ assert.ok(memoryRecords > 0, 'expected at least one memory_record');
32
+ }
33
+
34
+ return { schema: schemaValidation.value, records };
35
+ }
36
+
37
+ async function assertSourceAdapterContractFails(adapter, { source, pattern } = {}) {
38
+ let error = null;
39
+ try {
40
+ await assertSourceAdapterContract(adapter, { source });
41
+ } catch (err) {
42
+ error = err;
43
+ }
44
+ assert.ok(error, 'expected adapter contract to fail');
45
+ if (pattern) assert.match(error.message, pattern);
46
+ return error;
47
+ }
48
+
49
+ async function assertAdapterRejectsInvalidRecords(adapter, { source } = {}) {
50
+ await assert.rejects(async () => {
51
+ await collectIngestRecords(adapter, source);
52
+ }, SourceValidationError);
53
+ }
54
+
55
+ module.exports = {
56
+ assertAdapterRejectsInvalidRecords,
57
+ assertSourceAdapterContract,
58
+ assertSourceAdapterContractFails,
59
+ };
@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+
3
+ const { SourceAdapterBase } = require('./base');
4
+ const {
5
+ cleanUserText,
6
+ extractMessageText,
7
+ fileVersion,
8
+ looksLikeToolResult,
9
+ readJsonlFile,
10
+ sourceIdFromFile,
11
+ toIso,
12
+ } = require('./jsonl-utils');
13
+ const { buildExchangeRecords, messageToRecords } = require('./coding-session-utils');
14
+
15
+ class GeminiJsonlAdapter extends SourceAdapterBase {
16
+ static schema = {
17
+ adapterId: 'gemini-jsonl',
18
+ version: '1.0.0',
19
+ supportsIncremental: true,
20
+ defaultPrivacyClass: 'pii_potential',
21
+ declaredTransformations: [
22
+ 'newline_normalize',
23
+ 'strip_tool_chrome',
24
+ 'base64_payload_omit',
25
+ 'tool_result_summarize',
26
+ 'whitespace_trim',
27
+ 'exchange_pair_chunk',
28
+ ],
29
+ fields: {
30
+ sourceId: { type: 'string', required: true },
31
+ sourceFile: { type: 'string', required: true },
32
+ cwd: { type: 'string', required: false },
33
+ },
34
+ };
35
+
36
+ validateSource(source) {
37
+ const normalized = super.validateSource(source);
38
+ if (!source.sourceId) normalized.sourceId = sourceIdFromFile('gemini', normalized.sourceFile || normalized.uri);
39
+ return normalized;
40
+ }
41
+
42
+ async *ingest({ source, context }) {
43
+ const filePath = source.sourceFile || source.uri;
44
+ const version = fileVersion(filePath);
45
+ const { events, diagnostics } = readJsonlFile(filePath);
46
+ for (const diagnostic of diagnostics) context?.warn?.(diagnostic.message, diagnostic);
47
+
48
+ let cwd = source.cwd || '';
49
+ const messages = [];
50
+
51
+ for (const { line, event } of events) {
52
+ if (event.type === 'session_metadata' || event.session_metadata) {
53
+ const meta = event.session_metadata || event;
54
+ cwd = meta.cwd || meta.workspace || cwd;
55
+ continue;
56
+ }
57
+ const rawRole = event.role || event.type || event.author || event.sender;
58
+ const role = rawRole === 'model' || rawRole === 'gemini' || rawRole === 'assistant'
59
+ ? 'assistant'
60
+ : rawRole === 'user' ? 'user' : null;
61
+ if (!role) continue;
62
+ const content = role === 'user'
63
+ ? cleanUserText(extractMessageText(event))
64
+ : extractMessageText(event);
65
+ if (!content || looksLikeToolResult(content)) continue;
66
+
67
+ const message = {
68
+ itemId: event.id || event.uuid || `line-${line}`,
69
+ role,
70
+ content,
71
+ timestamp: toIso(event.timestamp || event.created_at),
72
+ cwd,
73
+ rawType: event.type || '',
74
+ harness: 'gemini',
75
+ toolCalls: [],
76
+ };
77
+ messages.push(message);
78
+ for (const record of messageToRecords({ source, message, version })) yield record;
79
+ }
80
+
81
+ for (const record of buildExchangeRecords(source, messages)) yield record;
82
+ }
83
+ }
84
+
85
+ module.exports = GeminiJsonlAdapter;
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ ...require('./base'),
5
+ ...require('./record-types'),
6
+ ...require('./transforms'),
7
+ ...require('./builtin'),
8
+ registry: require('./registry'),
9
+ };
@@ -0,0 +1,181 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const crypto = require('node:crypto');
6
+
7
+ const { applyTransforms } = require('./transforms');
8
+
9
+ function readJsonlFile(filePath) {
10
+ const events = [];
11
+ const diagnostics = [];
12
+ let raw = '';
13
+ try {
14
+ raw = fs.readFileSync(filePath, 'utf8');
15
+ } catch (err) {
16
+ return { events, diagnostics: [{ level: 'error', message: err.message, filePath }] };
17
+ }
18
+ const lines = raw.split('\n');
19
+ for (let i = 0; i < lines.length; i++) {
20
+ const line = lines[i];
21
+ if (!line.trim()) continue;
22
+ try {
23
+ events.push({ line: i + 1, event: JSON.parse(line) });
24
+ } catch (err) {
25
+ diagnostics.push({ level: 'warn', message: `Malformed JSONL line ${i + 1}: ${err.message}`, filePath, line: i + 1 });
26
+ }
27
+ }
28
+ return { events, diagnostics };
29
+ }
30
+
31
+ function fileVersion(filePath) {
32
+ try {
33
+ const stat = fs.statSync(filePath);
34
+ return `mtime:${Math.round(stat.mtimeMs)}:size:${stat.size}`;
35
+ } catch {
36
+ return 'missing';
37
+ }
38
+ }
39
+
40
+ function sourceIdFromFile(prefix, filePath) {
41
+ const base = path.basename(filePath || 'unknown', '.jsonl');
42
+ const hash = crypto.createHash('sha1').update(path.resolve(filePath || base)).digest('hex').slice(0, 12);
43
+ return `${prefix}:${base}:${hash}`;
44
+ }
45
+
46
+ function extractMessageText(value) {
47
+ if (value == null) return '';
48
+ if (typeof value === 'string') return value;
49
+ if (Array.isArray(value)) return extractBlocksText(value);
50
+ if (typeof value !== 'object') return String(value);
51
+ if (value.message?.content != null) return extractMessageText(value.message.content);
52
+ if (value.content != null) return extractMessageText(value.content);
53
+ if (value.text != null) return String(value.text);
54
+ if (value.parts != null) return extractMessageText(value.parts);
55
+ return '';
56
+ }
57
+
58
+ function extractBlocksText(blocks) {
59
+ const parts = [];
60
+ for (const block of blocks || []) {
61
+ if (!block) continue;
62
+ const type = block.type || '';
63
+ if (type === 'text' || type === 'input_text' || type === 'output_text') {
64
+ if (typeof block.text === 'string') parts.push(block.text);
65
+ continue;
66
+ }
67
+ if (type === 'tool_use' || type === 'function_call') {
68
+ const name = block.name || block.function?.name || 'tool';
69
+ parts.push(`[Tool: ${name}] ${summarizeToolInput(name, block.input ?? block.arguments)}`);
70
+ continue;
71
+ }
72
+ if (type === 'tool_result' || type === 'function_call_output') {
73
+ const content = block.content ?? block.output ?? '';
74
+ const text = extractMessageText(content);
75
+ if (text) parts.push(`[Tool result] ${text}`);
76
+ continue;
77
+ }
78
+ if (block.text) parts.push(String(block.text));
79
+ }
80
+ return parts.join('\n').trim();
81
+ }
82
+
83
+ function extractToolCalls(value) {
84
+ const calls = [];
85
+ const blocks = Array.isArray(value?.content) ? value.content
86
+ : Array.isArray(value) ? value
87
+ : [];
88
+ for (const block of blocks) {
89
+ if (!block || typeof block !== 'object') continue;
90
+ if (block.type === 'tool_use' || block.type === 'function_call') {
91
+ calls.push({
92
+ name: block.name || block.function?.name || 'tool',
93
+ input: parseToolInput(block.input ?? block.arguments ?? {}),
94
+ });
95
+ }
96
+ }
97
+ return calls;
98
+ }
99
+
100
+ function parseToolInput(value) {
101
+ if (value == null) return {};
102
+ if (typeof value === 'object') return value;
103
+ if (typeof value !== 'string') return { value: String(value) };
104
+ try {
105
+ const parsed = JSON.parse(value);
106
+ return parsed && typeof parsed === 'object' ? parsed : { value: parsed };
107
+ } catch {
108
+ return { raw: value };
109
+ }
110
+ }
111
+
112
+ function summarizeToolInput(toolName, input) {
113
+ const parsed = parseToolInput(input);
114
+ if (!parsed || typeof parsed !== 'object') return '';
115
+ const direct = parsed.file_path || parsed.filePath || parsed.path || parsed.command || parsed.cmd || parsed.pattern || parsed.query || parsed.raw;
116
+ if (direct) return truncate(String(direct), 160);
117
+ const keys = Object.keys(parsed);
118
+ if (keys.length === 0) return '';
119
+ return truncate(JSON.stringify(parsed), 160);
120
+ }
121
+
122
+ function cleanUserText(text) {
123
+ if (!text) return '';
124
+ return String(text)
125
+ .replace(/\r\n/g, '\n')
126
+ .replace(/^\s*<environment_context>[\s\S]*?<\/environment_context>\s*/gi, '')
127
+ .replace(/^\s*<permissions instructions>[\s\S]*?<\/permissions instructions>\s*/gi, '')
128
+ .replace(/^\s*<skills_instructions>[\s\S]*?<\/skills_instructions>\s*/gi, '')
129
+ .replace(/^\s*<plugins_instructions>[\s\S]*?<\/plugins_instructions>\s*/gi, '')
130
+ .replace(/^\s*<skill>[\s\S]*?<\/skill>\s*/gi, '')
131
+ .trim();
132
+ }
133
+
134
+ function looksLikeToolResult(text) {
135
+ const trimmed = String(text || '').trim();
136
+ if (!trimmed) return true;
137
+ const head = trimmed.slice(0, 600);
138
+ return [
139
+ /^\[Request interrupted/i,
140
+ /^<tool_use_error>/i,
141
+ /^<system-reminder>/i,
142
+ /^<local-command-/i,
143
+ /^<command-/i,
144
+ /^<task-notification>/i,
145
+ /^<environment_context>/i,
146
+ /^<permissions instructions>/i,
147
+ /^<skills_instructions>/i,
148
+ /^<plugins_instructions>/i,
149
+ /^Base directory for this skill:/i,
150
+ ].some((re) => re.test(head));
151
+ }
152
+
153
+ function cleanedContent(raw, transforms, context = {}) {
154
+ return applyTransforms(raw, transforms, context);
155
+ }
156
+
157
+ function truncate(value, max) {
158
+ const text = String(value || '');
159
+ return text.length > max ? `${text.slice(0, max - 3)}...` : text;
160
+ }
161
+
162
+ function toIso(value) {
163
+ if (!value) return new Date().toISOString();
164
+ const date = new Date(value);
165
+ return Number.isNaN(date.getTime()) ? new Date().toISOString() : date.toISOString();
166
+ }
167
+
168
+ module.exports = {
169
+ cleanUserText,
170
+ cleanedContent,
171
+ extractMessageText,
172
+ extractToolCalls,
173
+ fileVersion,
174
+ looksLikeToolResult,
175
+ parseToolInput,
176
+ readJsonlFile,
177
+ sourceIdFromFile,
178
+ summarizeToolInput,
179
+ toIso,
180
+ truncate,
181
+ };