agentbox-app 0.1.11 → 0.1.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 +24 -16
- package/bin/agentbox.mjs +18 -9
- package/briefs/message-rules.md +15 -4
- package/main/account-tooling.mjs +2 -1
- package/main/acp.mjs +296 -0
- package/main/agent-homes.mjs +158 -0
- package/main/agent-sessions.mjs +57 -20
- package/main/agent-updates.mjs +9 -20
- package/main/analytics.mjs +30 -0
- package/main/approvals.mjs +14 -5
- package/main/claude-bin.mjs +45 -310
- package/main/claude-input.mjs +9 -3
- package/main/claude-launch-env.mjs +54 -0
- package/main/claude-models.mjs +39 -7
- package/main/claude-plan.mjs +14 -2
- package/main/claude-usage.mjs +82 -25
- package/main/codex-bin.mjs +51 -248
- package/main/codex-launch-env.mjs +31 -0
- package/main/codex-session.mjs +38 -3
- package/main/codex-threads.mjs +41 -8
- package/main/codex-watch.mjs +67 -3
- package/main/config.mjs +40 -13
- package/main/corner-tag.mjs +489 -0
- package/main/crash-report.mjs +26 -3
- package/main/demo.mjs +17 -14
- package/main/engine-bin.mjs +595 -0
- package/main/engine-setup.mjs +27 -27
- package/main/feedback.mjs +11 -6
- package/main/fresh-user.mjs +1 -1
- package/main/git-change.mjs +8 -2
- package/main/harnesses/claude-stream.mjs +208 -0
- package/main/harnesses/claude.mjs +84 -0
- package/main/harnesses/codex.mjs +65 -0
- package/main/harnesses/common.mjs +78 -0
- package/main/harnesses/instructions-file.mjs +116 -0
- package/main/harnesses/opencode.mjs +234 -0
- package/main/harnesses.mjs +65 -0
- package/main/importable-threads.mjs +38 -0
- package/main/instruction-settings.mjs +47 -11
- package/main/ipc.mjs +175 -22
- package/main/local-app-frames.mjs +82 -0
- package/main/machine.mjs +1 -1
- package/main/main.mjs +120 -5
- package/main/memory-gate-history.mjs +51 -12
- package/main/memory-gate-server.mjs +67 -11
- package/main/memory-gate.mjs +49 -8
- package/main/memory-log.mjs +124 -0
- package/main/notify.mjs +48 -5
- package/main/opencode-server.mjs +271 -0
- package/main/opencode.mjs +287 -0
- package/main/phone-link.mjs +551 -0
- package/main/refresh-agent-models.mjs +2 -1
- package/main/relay-address.mjs +28 -0
- package/main/relay-client.mjs +246 -0
- package/main/row-label.mjs +8 -7
- package/main/runs-on.mjs +90 -0
- package/main/serve.mjs +124 -10
- package/main/settings.mjs +171 -11
- package/main/shell-path.mjs +86 -0
- package/main/ship-queue.mjs +25 -7
- package/main/sign-in-files.mjs +3 -7
- package/main/source-updater.mjs +27 -1
- package/main/start-hold.mjs +73 -0
- package/main/store/home.mjs +37 -5
- package/main/store/project.mjs +15 -19
- package/main/store.mjs +10 -4
- package/main/supervisor.mjs +1030 -447
- package/main/task-commands.mjs +13 -17
- package/main/task-folders-offthread.mjs +6 -1
- package/main/task-folders.mjs +491 -82
- package/main/task-terminals.mjs +49 -8
- package/main/team/index.mjs +2 -2
- package/main/team/projects.mjs +1 -1
- package/main/updater.mjs +71 -9
- package/main/worktree-include.mjs +494 -0
- package/mcp/core/page.mjs +1 -1
- package/mcp/core/work.mjs +34 -3
- package/mcp/tools.mjs +2 -1
- package/package.json +10 -1
- package/renderer/dist/assets/abnfDiagram-YUKMZFIV-DPPuMWt-.js +1 -0
- package/renderer/dist/assets/arc-HmFfdseH.js +1 -0
- package/renderer/dist/assets/architectureDiagram-47P4ROYG-DxJ3zHrH.js +36 -0
- package/renderer/dist/assets/blockDiagram-E7TT5TSB-CbTXzOqQ.js +140 -0
- package/renderer/dist/assets/c4Diagram-CRA5TL53-X5ItFyLn.js +38 -0
- package/renderer/dist/assets/channel-BlT2g9Sz.js +1 -0
- package/renderer/dist/assets/chunk-2Q5K7J3B-DC12Ho_L.js +1 -0
- package/renderer/dist/assets/chunk-5VM5RSS4-ClVtfPd0.js +15 -0
- package/renderer/dist/assets/chunk-7TKQ45FW-DgcOm0HT.js +1 -0
- package/renderer/dist/assets/chunk-AW2ZBBNX-C01s4yoF.js +88 -0
- package/renderer/dist/assets/chunk-FVRAUYC3-Bj0zxhR4.js +2 -0
- package/renderer/dist/assets/chunk-HTAEGDNF-DWDeEhLU.js +1 -0
- package/renderer/dist/assets/chunk-JWPE2WC7-Dsqwv3eW.js +1 -0
- package/renderer/dist/assets/chunk-KQW6MTUR-CE86ANL2.js +1 -0
- package/renderer/dist/assets/chunk-XXDRQBXY-CoNTASfs.js +1 -0
- package/renderer/dist/assets/classDiagram-v2-K4WV4PDN-2wprLqtw.js +217 -0
- package/renderer/dist/assets/cornerTag-BLrxiDE5.css +1 -0
- package/renderer/dist/assets/cornerTag-BOT6u3Tn.js +1 -0
- package/renderer/dist/assets/cose-bilkent-JH36ORCC-enqMiYbn.js +1 -0
- package/renderer/dist/assets/cynefin-EF2NZ3EQ-DHngLxvQ.js +141 -0
- package/renderer/dist/assets/cynefinDiagram-3GCD6N5R-C0rZZIl3.js +62 -0
- package/renderer/dist/assets/cytoscape.esm-C5JX3QLV.js +331 -0
- package/renderer/dist/assets/dagre-W4DXFKR2-DQnaEWO7.js +4 -0
- package/renderer/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/renderer/dist/assets/diagram-2UJZ2QOL-CS8AlGPx.js +200 -0
- package/renderer/dist/assets/diagram-OVF4WLC6-yiDMHIkY.js +24 -0
- package/renderer/dist/assets/diagram-PFPMY2P6-XbQKzbBA.js +30 -0
- package/renderer/dist/assets/diagram-UMYRVEAY-dhVvlfl_.js +24 -0
- package/renderer/dist/assets/diagram-YEKJPTXX-DZHe8SCf.js +41 -0
- package/renderer/dist/assets/diagram-ZIFT7M5P-B5h6aXVR.js +3 -0
- package/renderer/dist/assets/ebnfDiagram-VR2GEFS7-PR9ScDco.js +1 -0
- package/renderer/dist/assets/elk-IJKZMXRS-Cg-yQXPd.js +27 -0
- package/renderer/dist/assets/erDiagram-O2IAPWRE-omOrJMxu.js +99 -0
- package/renderer/dist/assets/flowDiagram-OXPTDLAJ-aOX2hzvT.js +213 -0
- package/renderer/dist/assets/ganttDiagram-R7TSEDQI-B1H3pWyI.js +292 -0
- package/renderer/dist/assets/gitGraphDiagram-XJZIOB7I-Cvw304HC.js +106 -0
- package/renderer/dist/assets/graph-DGbsTEsI.js +1 -0
- package/renderer/dist/assets/infoDiagram-5W2HQ5XZ-_ZXcNViH.js +2 -0
- package/renderer/dist/assets/init-Gi6I4Gst.js +1 -0
- package/renderer/dist/assets/ishikawaDiagram-K3B6WC7H-C7Mx_qQk.js +70 -0
- package/renderer/dist/assets/journeyDiagram-COXZFDF6-fr62QoAt.js +139 -0
- package/renderer/dist/assets/kanban-definition-P3RFRI5V-J8zsabCe.js +89 -0
- package/renderer/dist/assets/katex-C5jXJg4s.js +257 -0
- package/renderer/dist/assets/linear-BmNdEtIi.js +1 -0
- package/renderer/dist/assets/main-ChNdnUvt.js +1132 -0
- package/renderer/dist/assets/main-nj0QR5kg.css +32 -0
- package/renderer/dist/assets/mermaid.core-CnYzvFkz.js +314 -0
- package/renderer/dist/assets/mindmap-definition-LZFPQGKD-Bfcxc7PM.js +96 -0
- package/renderer/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/renderer/dist/assets/pegDiagram-BGZESJAR-CwUJOq6X.js +1 -0
- package/renderer/dist/assets/pieDiagram-CAPJLFHJ-BrwMmqfC.js +39 -0
- package/renderer/dist/assets/product-name-CTKLYNWY.js +40 -0
- package/renderer/dist/assets/quadrantDiagram-GDMTTRAM-BN4Stoj-.js +7 -0
- package/renderer/dist/assets/railroadDiagram-SM67HX2B-CPZ5bbeR.js +1 -0
- package/renderer/dist/assets/requirementDiagram-X7JNWC4A-CgRnimVe.js +84 -0
- package/renderer/dist/assets/sankeyDiagram-UM26HJRW-Bt2rej__.js +40 -0
- package/renderer/dist/assets/sequenceDiagram-ZO4K6R2Y-Dd5nKe9G.js +169 -0
- package/renderer/dist/assets/sizeCapture-INFHLROL-usyNyXyh.js +1 -0
- package/renderer/dist/assets/stateDiagram-v2-TBUQTH76-CRzUBb0u.js +291 -0
- package/renderer/dist/assets/swimlanes-N4OXWK64-C1GJvMS0.js +1 -0
- package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cy4Av2_p.js +8 -0
- package/renderer/dist/assets/timeline-definition-YOQAKGHF-caeyPlcc.js +120 -0
- package/renderer/dist/assets/usecaseDiagram-VIAY4XPW-BN_SV6PC.js +370 -0
- package/renderer/dist/assets/vennDiagram-BLWOH2XV-BLGuaUX-.js +34 -0
- package/renderer/dist/assets/wardleyDiagram-YMQ3BMBF-Da0cJeNc.js +78 -0
- package/renderer/dist/assets/xychartDiagram-TAQBALBS-cGfUffMk.js +7 -0
- package/renderer/dist/corner-tag.html +13 -0
- package/renderer/dist/emulator/audio-rules.mjs +74 -0
- package/renderer/dist/emulator/data/compression/README.md +11 -0
- package/renderer/dist/emulator/data/compression/extract7z.js +1 -0
- package/renderer/dist/emulator/data/compression/extractzip.js +1 -0
- package/renderer/dist/emulator/data/compression/libunrar.js +519 -0
- package/renderer/dist/emulator/data/compression/libunrar.wasm +0 -0
- package/renderer/dist/emulator/data/cores/README.md +22 -0
- package/renderer/dist/emulator/data/cores/mgba-legacy-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-thread-legacy-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-thread-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/package.json +65 -0
- package/renderer/dist/emulator/data/cores/reports/mgba.json +1 -0
- package/renderer/dist/emulator/data/emulator.css +1614 -0
- package/renderer/dist/emulator/data/loader.js +170 -0
- package/renderer/dist/emulator/data/localization/README.md +63 -0
- package/renderer/dist/emulator/data/localization/af-FR.json +339 -0
- package/renderer/dist/emulator/data/localization/ar-AR.json +301 -0
- package/renderer/dist/emulator/data/localization/ben-BEN.json +301 -0
- package/renderer/dist/emulator/data/localization/de-GER.json +301 -0
- package/renderer/dist/emulator/data/localization/el-GR.json +301 -0
- package/renderer/dist/emulator/data/localization/en-US.json +339 -0
- package/renderer/dist/emulator/data/localization/es-ES.json +310 -0
- package/renderer/dist/emulator/data/localization/fa-AF.json +310 -0
- package/renderer/dist/emulator/data/localization/hi-HI.json +301 -0
- package/renderer/dist/emulator/data/localization/it-IT.json +302 -0
- package/renderer/dist/emulator/data/localization/ja-JA.json +301 -0
- package/renderer/dist/emulator/data/localization/jv-JV.json +301 -0
- package/renderer/dist/emulator/data/localization/ko-KO.json +336 -0
- package/renderer/dist/emulator/data/localization/pt-BR.json +361 -0
- package/renderer/dist/emulator/data/localization/retroarch.json +617 -0
- package/renderer/dist/emulator/data/localization/ro-RO.json +310 -0
- package/renderer/dist/emulator/data/localization/ru-RU.json +301 -0
- package/renderer/dist/emulator/data/localization/tr-TR.json +310 -0
- package/renderer/dist/emulator/data/localization/vi-VN.json +302 -0
- package/renderer/dist/emulator/data/localization/zh-CN.json +351 -0
- package/renderer/dist/emulator/data/src/GameManager.js +464 -0
- package/renderer/dist/emulator/data/src/compression.js +142 -0
- package/renderer/dist/emulator/data/src/emulator.js +6158 -0
- package/renderer/dist/emulator/data/src/gamepad.js +167 -0
- package/renderer/dist/emulator/data/src/nipplejs.js +1 -0
- package/renderer/dist/emulator/data/src/shaders.js +236 -0
- package/renderer/dist/emulator/data/src/socket.io.min.js +7 -0
- package/renderer/dist/emulator/data/src/storage.js +125 -0
- package/renderer/dist/emulator/data/version.json +5 -0
- package/renderer/dist/emulator/host.js +297 -0
- package/renderer/dist/emulator/index.html +28 -0
- package/renderer/dist/emulator/provenance.json +6 -0
- package/renderer/dist/index.html +16 -3
- package/scripts/after-install.mjs +14 -0
- package/scripts/approval-server.sh +31 -7
- package/scripts/before-publish.mjs +12 -10
- package/shared/agent-import.mjs +3 -1
- package/shared/agents.d.mts +1 -0
- package/shared/bridge-map.mjs +19 -0
- package/shared/claude-commands.generated.mjs +8 -6
- package/shared/claude-models.generated.mjs +10 -8
- package/shared/codex-modes.mjs +1 -1
- package/shared/corner-tag.d.mts +14 -0
- package/shared/corner-tag.mjs +141 -0
- package/shared/demo-world.mjs +1 -1
- package/shared/engines.mjs +20 -5
- package/shared/fresh-user-home.mjs +12 -1
- package/shared/harness-definitions.mjs +103 -0
- package/shared/instruction-defaults.json +1 -1
- package/shared/notify-rules.d.mts +25 -0
- package/shared/notify-rules.mjs +19 -0
- package/shared/project-folder-check.mjs +3 -3
- package/shared/provider-commands.mjs +10 -2
- package/shared/rank.d.mts +2 -0
- package/shared/rank.mjs +50 -0
- package/shared/relay-address.mjs +6 -0
- package/shared/relay-crypto.mjs +165 -0
- package/shared/runs-on.d.mts +10 -0
- package/shared/runs-on.mjs +55 -0
- package/shared/shortcuts.mjs +11 -5
- package/shared/spawn-trouble.mjs +58 -7
- package/shared/terminal-input.d.mts +1 -0
- package/shared/terminal-input.mjs +30 -0
- package/shared/terminal-keys.d.mts +1 -0
- package/shared/terminal-keys.mjs +16 -0
- package/shared/terminal-size.d.mts +4 -0
- package/shared/terminal-size.mjs +29 -0
- package/shared/usage.d.mts +3 -0
- package/shared/usage.mjs +7 -0
- package/shared/where-it-runs-from.d.mts +43 -0
- package/shared/where-it-runs-from.mjs +155 -0
- package/shared/work-items.mjs +28 -2
- package/shared/work-lines.mjs +1 -1
- package/shared/xterm-invalid-range.d.mts +2 -0
- package/shared/xterm-invalid-range.mjs +58 -0
- package/renderer/dist/assets/index-B-2Az952.css +0 -32
- package/renderer/dist/assets/index-D6qncgai.js +0 -1168
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// READING AN OPENCODE RUN, IN OPENCODE'S OWN WORDS.
|
|
2
|
+
//
|
|
3
|
+
// The same five readers Claude Code and Codex each provide -- capture, live
|
|
4
|
+
// text, summary, trace, activity -- over OpenCode's `/event` stream. Nothing
|
|
5
|
+
// here translates an OpenCode event into a pretend Claude JSON line or a
|
|
6
|
+
// pretend Codex notification, for the reason docs/harnesses.md gives: the
|
|
7
|
+
// vocabularies are not the same shape and a fake one loses what the real one
|
|
8
|
+
// carries. OpenCode's `edit` approval, for instance, arrives with a whole
|
|
9
|
+
// unified diff in it, which neither of the other two does.
|
|
10
|
+
//
|
|
11
|
+
// EVERY SHAPE BELOW WAS READ OFF A RUNNING OPENCODE 1.18.35 ON 2026-10-07,
|
|
12
|
+
// not off the docs, which do not enumerate the event payloads at all. The
|
|
13
|
+
// ones that matter:
|
|
14
|
+
//
|
|
15
|
+
// session.updated properties.info.{cost,tokens,title,model,directory}
|
|
16
|
+
// message.part.updated properties.part.type: text | reasoning | tool |
|
|
17
|
+
// step-start | step-finish
|
|
18
|
+
// message.part.delta properties.{partID,field,delta} ("field":"text")
|
|
19
|
+
// permission.asked properties.{id,sessionID,permission,patterns,
|
|
20
|
+
// metadata:{command} | metadata:{filepath,diff}}
|
|
21
|
+
// session.idle properties.sessionID
|
|
22
|
+
// session.error properties.{sessionID,error}
|
|
23
|
+
//
|
|
24
|
+
// A tool part carries `state.status` of pending | running | completed | error,
|
|
25
|
+
// with `state.input`, `state.output` and `state.error` beside it.
|
|
26
|
+
|
|
27
|
+
/** How much streamed prose is kept before it is dropped, as the others do. */
|
|
28
|
+
const SAYING_CAP = 20_000;
|
|
29
|
+
|
|
30
|
+
/** Tool names worth naming on the row, in the words a person would use. */
|
|
31
|
+
const TOOL_WORDS = {
|
|
32
|
+
bash: 'Bash', edit: 'Edit', write: 'Write', read: 'Read', glob: 'Glob',
|
|
33
|
+
grep: 'Grep', list: 'List', webfetch: 'Fetch', websearch: 'Search',
|
|
34
|
+
task: 'Task', todowrite: 'Todo', patch: 'Edit', skill: 'Skill',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const toolWord = name => {
|
|
38
|
+
const id = String(name ?? '').trim();
|
|
39
|
+
if (!id) return null;
|
|
40
|
+
return TOOL_WORDS[id.toLowerCase()] ?? id;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const partOf = properties => (properties && typeof properties === 'object' ? properties.part ?? null : null);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* WHAT THE RUN WAS, kept on the session as it goes.
|
|
47
|
+
*
|
|
48
|
+
* `sessionId` is the one OpenCode resumes by, and it is on nearly every event
|
|
49
|
+
* as `properties.sessionID`. It is taken from the first event that carries one
|
|
50
|
+
* rather than from the session-creation response, so a resumed run and a fresh
|
|
51
|
+
* one record it the same way.
|
|
52
|
+
*
|
|
53
|
+
* The result is the LAST assistant text of the turn. OpenCode has no single
|
|
54
|
+
* "here is the answer" event the way Codex's `turn/completed` does: prose
|
|
55
|
+
* arrives as `text` parts that are updated in place, and the final one is the
|
|
56
|
+
* answer. So each completed text part overwrites the last, and whatever is
|
|
57
|
+
* standing when the session goes idle is the result.
|
|
58
|
+
*/
|
|
59
|
+
export function captureOpenCodeEvent(session, type, properties) {
|
|
60
|
+
try {
|
|
61
|
+
if (!session || typeof type !== 'string') return;
|
|
62
|
+
const p = properties ?? {};
|
|
63
|
+
if (!session.sessionId && typeof p.sessionID === 'string' && p.sessionID) session.sessionId = p.sessionID;
|
|
64
|
+
|
|
65
|
+
if (type === 'session.updated') {
|
|
66
|
+
const info = p.info ?? {};
|
|
67
|
+
if (Number.isFinite(info.cost)) session.opencodeCost = info.cost;
|
|
68
|
+
if (info.tokens && typeof info.tokens === 'object') session.opencodeTokens = info.tokens;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (type === 'session.error') {
|
|
73
|
+
session.result = openCodeErrorText(p.error) ?? 'OpenCode stopped with an error it did not name.';
|
|
74
|
+
session.resultIsError = true;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (type === 'message.part.updated') {
|
|
79
|
+
const part = partOf(p);
|
|
80
|
+
if (!part) return;
|
|
81
|
+
// A WRITE THIS RUN MADE, so the card can say what changed. The `edit` and
|
|
82
|
+
// `write` tools report the file they touched in `state.input.filePath`,
|
|
83
|
+
// which is the only place a path appears for a change OpenCode has
|
|
84
|
+
// already applied.
|
|
85
|
+
if (part.type === 'tool' && part.state?.status === 'completed') {
|
|
86
|
+
const path = part.state?.input?.filePath ?? part.state?.input?.path;
|
|
87
|
+
if (typeof path === 'string' && path && (part.tool === 'edit' || part.tool === 'write' || part.tool === 'patch')) {
|
|
88
|
+
session.opencodeChange ??= [];
|
|
89
|
+
if (!session.opencodeChange.includes(path)) session.opencodeChange.push(path);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (part.type === 'step-finish') {
|
|
93
|
+
if (Number.isFinite(part.cost)) session.opencodeStepCost = part.cost;
|
|
94
|
+
if (part.tokens && typeof part.tokens === 'object') session.opencodeTokens = part.tokens;
|
|
95
|
+
}
|
|
96
|
+
// Only the assistant's prose is an answer. `reasoning` is the model
|
|
97
|
+
// thinking out loud and is never the result, which is why it is matched
|
|
98
|
+
// by name here rather than falling through to the text branch.
|
|
99
|
+
if (part.type === 'text') {
|
|
100
|
+
const text = String(part.text ?? '').trim();
|
|
101
|
+
if (text) { session.result = text; session.resultIsError = false; }
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
// A reader that throws kills a run that was going fine. Same rule as the
|
|
107
|
+
// other two harnesses: read what parses and ignore what does not.
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The error on `session.error`, which is an object and not a string. */
|
|
112
|
+
export function openCodeErrorText(error) {
|
|
113
|
+
if (!error) return null;
|
|
114
|
+
if (typeof error === 'string') return error.trim() || null;
|
|
115
|
+
const name = typeof error.name === 'string' ? error.name : '';
|
|
116
|
+
const message = typeof error.data?.message === 'string' ? error.data.message
|
|
117
|
+
: typeof error.message === 'string' ? error.message : '';
|
|
118
|
+
const joined = [name, message].filter(Boolean).join(': ');
|
|
119
|
+
return joined || null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* THE PROSE AS IT IS TYPED, for the line on the row that moves.
|
|
124
|
+
*
|
|
125
|
+
* Deltas arrive as `message.part.delta` with a `field` saying which field of
|
|
126
|
+
* which part is growing. Only `text` is shown: `reasoning` deltas are the
|
|
127
|
+
* model's private thinking, and streaming those onto her row would put words
|
|
128
|
+
* on screen that are not what the run decided.
|
|
129
|
+
*/
|
|
130
|
+
export function openCodeStreamingText(session, type, properties) {
|
|
131
|
+
try {
|
|
132
|
+
if (!session) return false;
|
|
133
|
+
const p = properties ?? {};
|
|
134
|
+
|
|
135
|
+
if (type === 'message.part.updated') {
|
|
136
|
+
const part = partOf(p);
|
|
137
|
+
// A tool starting means the talking stopped. What is already typed stays
|
|
138
|
+
// until the trace carries the same words, exactly as Codex's reader does.
|
|
139
|
+
if (part?.type === 'text' && !String(part.text ?? '')) { session.saying = ''; session.sayingAt = Date.now(); return true; }
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (type !== 'message.part.delta') return false;
|
|
144
|
+
if (p.field !== 'text') return false;
|
|
145
|
+
const piece = String(p.delta ?? '');
|
|
146
|
+
if (!piece) return false;
|
|
147
|
+
// A delta for a reasoning part has the same `field: "text"`, so the part it
|
|
148
|
+
// belongs to is what tells them apart. Without a remembered prose part id
|
|
149
|
+
// nothing is streamed, which is the quiet answer rather than the wrong one.
|
|
150
|
+
if (session.opencodeProsePart && p.partID && p.partID !== session.opencodeProsePart) return false;
|
|
151
|
+
if (!session.sayingAt) session.sayingAt = Date.now();
|
|
152
|
+
const next = (session.saying ?? '') + piece;
|
|
153
|
+
session.saying = next.length > SAYING_CAP ? '' : next;
|
|
154
|
+
return true;
|
|
155
|
+
} catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Which part the prose is going into, so reasoning deltas can be told apart. */
|
|
161
|
+
export function rememberOpenCodeProsePart(session, type, properties) {
|
|
162
|
+
if (!session || type !== 'message.part.updated') return;
|
|
163
|
+
const part = partOf(properties);
|
|
164
|
+
if (part?.type === 'text' && typeof part.id === 'string') session.opencodeProsePart = part.id;
|
|
165
|
+
if (part?.type === 'step-start') session.opencodeProsePart = null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** One short line for the row: what it just did. */
|
|
169
|
+
export function summarizeOpenCodeEvent(type, properties) {
|
|
170
|
+
try {
|
|
171
|
+
const p = properties ?? {};
|
|
172
|
+
if (type !== 'message.part.updated') return null;
|
|
173
|
+
const part = partOf(p);
|
|
174
|
+
if (!part) return null;
|
|
175
|
+
if (part.type === 'text') {
|
|
176
|
+
const text = String(part.text ?? '').trim();
|
|
177
|
+
return text ? text.slice(0, 300) : null;
|
|
178
|
+
}
|
|
179
|
+
if (part.type === 'tool' && part.state?.status === 'completed') {
|
|
180
|
+
const word = toolWord(part.tool);
|
|
181
|
+
return word ? `tool: ${word}` : null;
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
} catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** One line for the run trace, the record she can open afterwards. */
|
|
190
|
+
export function traceOpenCodeEvent(type, properties) {
|
|
191
|
+
try {
|
|
192
|
+
const p = properties ?? {};
|
|
193
|
+
const at = new Date().toISOString().slice(11, 19);
|
|
194
|
+
|
|
195
|
+
if (type === 'session.error') {
|
|
196
|
+
const text = openCodeErrorText(p.error);
|
|
197
|
+
return text ? `${at} [error] ${text.slice(0, 300)}` : null;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (type !== 'message.part.updated') return null;
|
|
201
|
+
const part = partOf(p);
|
|
202
|
+
if (!part) return null;
|
|
203
|
+
|
|
204
|
+
if (part.type === 'text') {
|
|
205
|
+
const text = String(part.text ?? '').trim();
|
|
206
|
+
return text ? `${at} ${text}` : null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (part.type === 'tool' && (part.state?.status === 'completed' || part.state?.status === 'error')) {
|
|
210
|
+
const word = toolWord(part.tool);
|
|
211
|
+
if (!word) return null;
|
|
212
|
+
const input = part.state?.input ?? {};
|
|
213
|
+
const detail = String(input.command ?? input.filePath ?? input.path ?? input.pattern ?? '').trim();
|
|
214
|
+
const failed = part.state?.status === 'error' ? ' (refused)' : '';
|
|
215
|
+
return `${at} [${word}]${failed}${detail ? ` ${detail.slice(0, 200)}` : ''}`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return null;
|
|
219
|
+
} catch {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** What it is doing right now, for the row's activity line. */
|
|
225
|
+
export function openCodeActivity(type, properties) {
|
|
226
|
+
try {
|
|
227
|
+
if (type !== 'message.part.updated') return null;
|
|
228
|
+
const part = partOf(properties);
|
|
229
|
+
if (part?.type !== 'tool') return null;
|
|
230
|
+
if (part.state?.status !== 'pending' && part.state?.status !== 'running') return null;
|
|
231
|
+
const word = toolWord(part.tool);
|
|
232
|
+
return word ? { tool: word } : null;
|
|
233
|
+
} catch {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* THE APPROVAL, IN THE WORDS A CARD NEEDS.
|
|
240
|
+
*
|
|
241
|
+
* `bash` asks carry `metadata.command`. `edit` asks carry `metadata.filepath`
|
|
242
|
+
* AND `metadata.diff`, a whole unified diff of the change it wants to make --
|
|
243
|
+
* measured, not assumed. That is strictly more than a Codex patch approval
|
|
244
|
+
* arrives with, and it means an OpenCode card can show the change itself
|
|
245
|
+
* rather than a filename.
|
|
246
|
+
*/
|
|
247
|
+
export function openCodeApprovalCard(properties) {
|
|
248
|
+
const p = properties ?? {};
|
|
249
|
+
const kind = typeof p.permission === 'string' ? p.permission : '';
|
|
250
|
+
const metadata = p.metadata ?? {};
|
|
251
|
+
const command = typeof metadata.command === 'string' ? metadata.command.trim() : '';
|
|
252
|
+
const filepath = typeof metadata.filepath === 'string' ? metadata.filepath.trim() : '';
|
|
253
|
+
const diff = typeof metadata.diff === 'string' ? metadata.diff : '';
|
|
254
|
+
const patterns = Array.isArray(p.patterns) ? p.patterns.filter(x => typeof x === 'string') : [];
|
|
255
|
+
|
|
256
|
+
if (command) return { kind: kind || 'bash', title: 'Run a command', detail: command, command, patterns };
|
|
257
|
+
if (filepath) return { kind: kind || 'edit', title: 'Change a file', detail: filepath, filepath, diff, patterns };
|
|
258
|
+
return { kind: kind || 'tool', title: 'Use a tool', detail: patterns.join(', ') || kind || 'a tool', patterns };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* THE PERMISSION POLICY A WORKER RUNS UNDER, as OpenCode's own config.
|
|
263
|
+
*
|
|
264
|
+
* This is the whole reason Agentbox can draw a card for OpenCode at all.
|
|
265
|
+
* Measured 2026-10-07: with no permission config the server runs shell
|
|
266
|
+
* commands and writes files with no card; with this config it raises
|
|
267
|
+
* `permission.asked` and waits. The server reports back what it loaded at
|
|
268
|
+
* `GET /config`, which is how the adapter can tell the policy really took.
|
|
269
|
+
*
|
|
270
|
+
* `read`, `glob`, `grep` and `list` are deliberately left alone: a card for
|
|
271
|
+
* every file read is the 2026-08-05 card flood again, and reading inside the
|
|
272
|
+
* task's own folder is what the folder is for.
|
|
273
|
+
*/
|
|
274
|
+
export function openCodePermissionConfig() {
|
|
275
|
+
return {
|
|
276
|
+
$schema: 'https://opencode.ai/config.json',
|
|
277
|
+
permission: {
|
|
278
|
+
bash: 'ask',
|
|
279
|
+
edit: 'ask',
|
|
280
|
+
webfetch: 'ask',
|
|
281
|
+
websearch: 'ask',
|
|
282
|
+
external_directory: 'ask',
|
|
283
|
+
task: 'allow',
|
|
284
|
+
todowrite: 'allow',
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
}
|