@adecore/agents 0.0.1 → 0.17.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +110 -0
- package/README.md +33 -0
- package/dist/async.d.ts +2 -0
- package/dist/async.js +31 -0
- package/dist/chat/attachment-store.d.ts +10 -0
- package/dist/chat/attachment-store.js +93 -0
- package/dist/chat/backend.d.ts +214 -0
- package/dist/chat/backend.js +1 -0
- package/dist/chat/background-work.d.ts +8 -0
- package/dist/chat/background-work.js +33 -0
- package/dist/chat/bookmark-store.d.ts +25 -0
- package/dist/chat/bookmark-store.js +141 -0
- package/dist/chat/chat-core.d.ts +311 -0
- package/dist/chat/chat-core.js +1025 -0
- package/dist/chat/chat-log.d.ts +33 -0
- package/dist/chat/chat-log.js +163 -0
- package/dist/chat/chat-process.d.ts +46 -0
- package/dist/chat/chat-process.js +190 -0
- package/dist/chat/chat-session.d.ts +209 -0
- package/dist/chat/chat-session.js +1511 -0
- package/dist/chat/chat-store.d.ts +37 -0
- package/dist/chat/chat-store.js +202 -0
- package/dist/chat/chat-title.d.ts +10 -0
- package/dist/chat/chat-title.js +74 -0
- package/dist/chat/claude-backend.d.ts +35 -0
- package/dist/chat/claude-backend.js +214 -0
- package/dist/chat/claude-protocol.d.ts +41 -0
- package/dist/chat/claude-protocol.js +642 -0
- package/dist/chat/claude-title.d.ts +11 -0
- package/dist/chat/claude-title.js +97 -0
- package/dist/chat/claude-transcript.d.ts +28 -0
- package/dist/chat/claude-transcript.js +211 -0
- package/dist/chat/codex-backend.d.ts +51 -0
- package/dist/chat/codex-backend.js +364 -0
- package/dist/chat/codex-protocol.d.ts +56 -0
- package/dist/chat/codex-protocol.js +767 -0
- package/dist/chat/codex-thread.d.ts +32 -0
- package/dist/chat/codex-thread.js +131 -0
- package/dist/chat/codex-transport.d.ts +42 -0
- package/dist/chat/codex-transport.js +188 -0
- package/dist/chat/composer-preferences.d.ts +15 -0
- package/dist/chat/composer-preferences.js +46 -0
- package/dist/chat/context-breakdown.d.ts +2 -0
- package/dist/chat/context-breakdown.js +74 -0
- package/dist/chat/delta-coalescer.d.ts +12 -0
- package/dist/chat/delta-coalescer.js +55 -0
- package/dist/chat/errors.d.ts +4 -0
- package/dist/chat/errors.js +4 -0
- package/dist/chat/fake-claude.d.ts +3 -0
- package/dist/chat/fake-claude.js +982 -0
- package/dist/chat/fake-cli.d.ts +25 -0
- package/dist/chat/fake-cli.js +157 -0
- package/dist/chat/fake-codex.d.ts +10 -0
- package/dist/chat/fake-codex.js +502 -0
- package/dist/chat/fixtures/claude-2.1.285-auto-fallback.json +24 -0
- package/dist/chat/fixtures/claude-2.1.285-native-children.json +6600 -0
- package/dist/chat/fixtures/claude-2.1.285-resume-direct-compact.json +202 -0
- package/dist/chat/fixtures/claude-2.1.285-resume-task-notification.json +57 -0
- package/dist/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a4c2e8f10b3d5a7e9.jsonl +10 -0
- package/dist/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a4c2e8f10b3d5a7e9.meta.json +1 -0
- package/dist/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a9b8c7d6e5f4a3b21.jsonl +2 -0
- package/dist/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a9b8c7d6e5f4a3b21.meta.json +1 -0
- package/dist/chat/fixtures/codex-0.159.2-native-approval.json +119 -0
- package/dist/chat/fixtures/codex-0.159.2-native-file-approval.json +136 -0
- package/dist/chat/fixtures/codex-0.159.2-native-interacted.json +220 -0
- package/dist/chat/fixtures/codex-0.159.2-usage-windows.json +74 -0
- package/dist/chat/input.d.ts +19 -0
- package/dist/chat/input.js +71 -0
- package/dist/chat/limit-resume.d.ts +11 -0
- package/dist/chat/limit-resume.js +68 -0
- package/dist/chat/projector.d.ts +60 -0
- package/dist/chat/projector.js +860 -0
- package/dist/chat/request-summary.d.ts +3 -0
- package/dist/chat/request-summary.js +143 -0
- package/dist/chat/subagent-projection.d.ts +8 -0
- package/dist/chat/subagent-projection.js +40 -0
- package/dist/chat/subagent-reader.d.ts +66 -0
- package/dist/chat/subagent-reader.js +396 -0
- package/dist/chat/subagent-settlement.d.ts +6 -0
- package/dist/chat/subagent-settlement.js +103 -0
- package/dist/chat/thread.d.ts +30 -0
- package/dist/chat/thread.js +147 -0
- package/dist/chat/wake-chat.d.ts +20 -0
- package/dist/chat/wake-chat.js +27 -0
- package/dist/client-sinks.d.ts +9 -0
- package/dist/client-sinks.js +34 -0
- package/dist/coded-error.d.ts +5 -0
- package/dist/coded-error.js +17 -0
- package/dist/context/argv.d.ts +11 -0
- package/dist/context/argv.js +58 -0
- package/dist/context/refusal.d.ts +9 -0
- package/dist/context/refusal.js +38 -0
- package/dist/context/verb.d.ts +85 -0
- package/dist/context/verb.js +233 -0
- package/dist/error-text.d.ts +5 -0
- package/dist/error-text.js +24 -0
- package/dist/events.d.ts +12 -0
- package/dist/events.js +1 -0
- package/dist/fs.d.ts +9 -0
- package/dist/fs.js +107 -0
- package/dist/host/agent-host.d.ts +28 -0
- package/dist/host/agent-host.js +105 -0
- package/dist/host/environment.d.ts +7 -0
- package/dist/host/environment.js +22 -0
- package/dist/host/handlers.d.ts +30 -0
- package/dist/host/handlers.js +111 -0
- package/dist/host/memory-port.d.ts +2 -0
- package/dist/host/memory-port.js +25 -0
- package/dist/host/wiring.d.ts +40 -0
- package/dist/host/wiring.js +120 -0
- package/dist/lineage.d.ts +44 -0
- package/dist/lineage.js +134 -0
- package/dist/messages/deliver-message.d.ts +21 -0
- package/dist/messages/deliver-message.js +42 -0
- package/dist/messages/deliver-notice.d.ts +25 -0
- package/dist/messages/deliver-notice.js +75 -0
- package/dist/messages/notice-notes.d.ts +15 -0
- package/dist/messages/notice-notes.js +26 -0
- package/dist/messages/notice-store.d.ts +30 -0
- package/dist/messages/notice-store.js +120 -0
- package/dist/modes.d.ts +11 -0
- package/dist/modes.js +28 -0
- package/dist/outbox/manual-clock.d.ts +10 -0
- package/dist/outbox/manual-clock.js +25 -0
- package/dist/outbox/outbox-worker.d.ts +44 -0
- package/dist/outbox/outbox-worker.js +173 -0
- package/dist/outbox/outbox.d.ts +37 -0
- package/dist/outbox/outbox.js +76 -0
- package/dist/providers/accounts/accounts.d.ts +20 -0
- package/dist/providers/accounts/accounts.js +158 -0
- package/dist/providers/accounts/launch.d.ts +17 -0
- package/dist/providers/accounts/launch.js +30 -0
- package/dist/providers/accounts/service.d.ts +87 -0
- package/dist/providers/accounts/service.js +538 -0
- package/dist/providers/accounts/shadow-home.d.ts +8 -0
- package/dist/providers/accounts/shadow-home.js +70 -0
- package/dist/providers/accounts/status.d.ts +12 -0
- package/dist/providers/accounts/status.js +85 -0
- package/dist/providers/accounts/store.d.ts +14 -0
- package/dist/providers/accounts/store.js +112 -0
- package/dist/providers/accounts/test-accounts.d.ts +12 -0
- package/dist/providers/accounts/test-accounts.js +38 -0
- package/dist/providers/accounts/variables.d.ts +27 -0
- package/dist/providers/accounts/variables.js +94 -0
- package/dist/providers/catalog.d.ts +17 -0
- package/dist/providers/catalog.js +76 -0
- package/dist/providers/claude-models.json +114 -0
- package/dist/providers/claude-provider.d.ts +4 -0
- package/dist/providers/claude-provider.js +29 -0
- package/dist/providers/claude.d.ts +15 -0
- package/dist/providers/claude.js +105 -0
- package/dist/providers/codex-models.json +135 -0
- package/dist/providers/codex-provider.d.ts +4 -0
- package/dist/providers/codex-provider.js +27 -0
- package/dist/providers/codex-rules.d.ts +9 -0
- package/dist/providers/codex-rules.js +35 -0
- package/dist/providers/codex.d.ts +14 -0
- package/dist/providers/codex.js +52 -0
- package/dist/providers/detect.d.ts +6 -0
- package/dist/providers/detect.js +30 -0
- package/dist/providers/provider.d.ts +25 -0
- package/dist/providers/provider.js +1 -0
- package/dist/providers/registry.d.ts +28 -0
- package/dist/providers/registry.js +118 -0
- package/dist/record-directory.d.ts +27 -0
- package/dist/record-directory.js +114 -0
- package/dist/run-process.d.ts +13 -0
- package/dist/run-process.js +42 -0
- package/dist/serializer.d.ts +9 -0
- package/dist/serializer.js +34 -0
- package/dist/skills.d.ts +21 -0
- package/dist/skills.js +174 -0
- package/dist/tasks/background-limit.d.ts +16 -0
- package/dist/tasks/background-limit.js +55 -0
- package/dist/tasks/end-children.d.ts +34 -0
- package/dist/tasks/end-children.js +53 -0
- package/dist/tasks/give-task.d.ts +14 -0
- package/dist/tasks/give-task.js +33 -0
- package/dist/tasks/task-coordinator.d.ts +52 -0
- package/dist/tasks/task-coordinator.js +323 -0
- package/dist/tasks/task-store.d.ts +49 -0
- package/dist/tasks/task-store.js +197 -0
- package/dist/tasks/task-work.d.ts +47 -0
- package/dist/tasks/task-work.js +28 -0
- package/dist/tasks/waiting-child.d.ts +51 -0
- package/dist/tasks/waiting-child.js +143 -0
- package/dist/tasks/wake-parent.d.ts +19 -0
- package/dist/tasks/wake-parent.js +109 -0
- package/dist/tasks/wiring.d.ts +51 -0
- package/dist/tasks/wiring.js +151 -0
- package/dist/title-file.d.ts +2 -0
- package/dist/title-file.js +55 -0
- package/dist/usage/accounts.d.ts +15 -0
- package/dist/usage/accounts.js +64 -0
- package/dist/usage/aggregate.d.ts +12 -0
- package/dist/usage/aggregate.js +154 -0
- package/dist/usage/exchange.d.ts +16 -0
- package/dist/usage/exchange.js +84 -0
- package/dist/usage/index-file.d.ts +15 -0
- package/dist/usage/index-file.js +123 -0
- package/dist/usage/limits/monitor.d.ts +50 -0
- package/dist/usage/limits/monitor.js +179 -0
- package/dist/usage/limits/normalize.d.ts +21 -0
- package/dist/usage/limits/normalize.js +160 -0
- package/dist/usage/limits/probe.d.ts +9 -0
- package/dist/usage/limits/probe.js +113 -0
- package/dist/usage/prices-snapshot.json +1071 -0
- package/dist/usage/pricing.d.ts +34 -0
- package/dist/usage/pricing.js +186 -0
- package/dist/usage/projects.d.ts +17 -0
- package/dist/usage/projects.js +49 -0
- package/dist/usage/readers/claude.d.ts +3 -0
- package/dist/usage/readers/claude.js +59 -0
- package/dist/usage/readers/codex.d.ts +23 -0
- package/dist/usage/readers/codex.js +155 -0
- package/dist/usage/record.d.ts +15 -0
- package/dist/usage/record.js +39 -0
- package/dist/usage/roots.d.ts +12 -0
- package/dist/usage/roots.js +29 -0
- package/dist/usage/scanner.d.ts +30 -0
- package/dist/usage/scanner.js +234 -0
- package/dist/usage/usage-service.d.ts +35 -0
- package/dist/usage/usage-service.js +130 -0
- package/dist/watch-seam.d.ts +29 -0
- package/dist/watch-seam.js +78 -0
- package/dist/watch-test-helpers.d.ts +28 -0
- package/dist/watch-test-helpers.js +86 -0
- package/package.json +42 -3
- package/src/async.ts +40 -0
- package/src/chat/attachment-store.ts +108 -0
- package/src/chat/backend.ts +199 -0
- package/src/chat/background-work.ts +42 -0
- package/src/chat/bookmark-store.ts +166 -0
- package/src/chat/chat-core.ts +1228 -0
- package/src/chat/chat-log.ts +187 -0
- package/src/chat/chat-process.ts +237 -0
- package/src/chat/chat-session.ts +1723 -0
- package/src/chat/chat-store.ts +261 -0
- package/src/chat/chat-title.ts +90 -0
- package/src/chat/claude-backend.ts +235 -0
- package/src/chat/claude-protocol.ts +684 -0
- package/src/chat/claude-title.ts +106 -0
- package/src/chat/claude-transcript.ts +233 -0
- package/src/chat/codex-backend.ts +397 -0
- package/src/chat/codex-protocol.ts +872 -0
- package/src/chat/codex-thread.ts +162 -0
- package/src/chat/codex-transport.ts +224 -0
- package/src/chat/composer-preferences.ts +67 -0
- package/src/chat/context-breakdown.ts +80 -0
- package/src/chat/delta-coalescer.ts +62 -0
- package/src/chat/errors.ts +25 -0
- package/src/chat/fake-claude.ts +1006 -0
- package/src/chat/fake-cli.ts +199 -0
- package/src/chat/fake-codex.ts +534 -0
- package/src/chat/fixtures/claude-2.1.285-auto-fallback.json +24 -0
- package/src/chat/fixtures/claude-2.1.285-native-children.json +6600 -0
- package/src/chat/fixtures/claude-2.1.285-resume-direct-compact.json +202 -0
- package/src/chat/fixtures/claude-2.1.285-resume-task-notification.json +57 -0
- package/src/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a4c2e8f10b3d5a7e9.jsonl +10 -0
- package/src/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a4c2e8f10b3d5a7e9.meta.json +1 -0
- package/src/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a9b8c7d6e5f4a3b21.jsonl +2 -0
- package/src/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a9b8c7d6e5f4a3b21.meta.json +1 -0
- package/src/chat/fixtures/codex-0.159.2-native-approval.json +119 -0
- package/src/chat/fixtures/codex-0.159.2-native-file-approval.json +136 -0
- package/src/chat/fixtures/codex-0.159.2-native-interacted.json +220 -0
- package/src/chat/fixtures/codex-0.159.2-usage-windows.json +74 -0
- package/src/chat/input.ts +89 -0
- package/src/chat/limit-resume.ts +74 -0
- package/src/chat/projector.ts +969 -0
- package/src/chat/request-summary.ts +168 -0
- package/src/chat/subagent-projection.ts +45 -0
- package/src/chat/subagent-reader.ts +469 -0
- package/src/chat/subagent-settlement.ts +111 -0
- package/src/chat/thread.ts +167 -0
- package/src/chat/wake-chat.ts +45 -0
- package/src/client-sinks.ts +39 -0
- package/src/coded-error.ts +18 -0
- package/src/context/argv.ts +61 -0
- package/src/context/refusal.ts +50 -0
- package/src/context/verb.ts +392 -0
- package/src/error-text.ts +28 -0
- package/src/events.ts +9 -0
- package/src/fs.ts +118 -0
- package/src/host/agent-host.ts +124 -0
- package/src/host/environment.ts +31 -0
- package/src/host/handlers.ts +142 -0
- package/src/host/memory-port.ts +27 -0
- package/src/host/wiring.ts +164 -0
- package/src/lineage.ts +159 -0
- package/src/messages/deliver-message.ts +61 -0
- package/src/messages/deliver-notice.ts +120 -0
- package/src/messages/notice-notes.ts +34 -0
- package/src/messages/notice-store.ts +142 -0
- package/src/modes.ts +38 -0
- package/src/outbox/manual-clock.ts +31 -0
- package/src/outbox/outbox-worker.ts +214 -0
- package/src/outbox/outbox.ts +106 -0
- package/src/providers/accounts/accounts.ts +192 -0
- package/src/providers/accounts/launch.ts +55 -0
- package/src/providers/accounts/service.ts +632 -0
- package/src/providers/accounts/shadow-home.ts +85 -0
- package/src/providers/accounts/status.ts +106 -0
- package/src/providers/accounts/store.ts +158 -0
- package/src/providers/accounts/test-accounts.ts +48 -0
- package/src/providers/accounts/variables.ts +132 -0
- package/src/providers/catalog.ts +90 -0
- package/src/providers/claude-models.json +114 -0
- package/src/providers/claude-provider.ts +32 -0
- package/src/providers/claude.ts +123 -0
- package/src/providers/codex-models.json +135 -0
- package/src/providers/codex-provider.ts +30 -0
- package/src/providers/codex-rules.ts +54 -0
- package/src/providers/codex.ts +69 -0
- package/src/providers/detect.ts +40 -0
- package/src/providers/provider.ts +46 -0
- package/src/providers/registry.ts +150 -0
- package/src/record-directory.ts +136 -0
- package/src/run-process.ts +61 -0
- package/src/serializer.ts +41 -0
- package/src/skills.ts +199 -0
- package/src/tasks/background-limit.ts +72 -0
- package/src/tasks/end-children.ts +100 -0
- package/src/tasks/give-task.ts +52 -0
- package/src/tasks/task-coordinator.ts +384 -0
- package/src/tasks/task-store.ts +236 -0
- package/src/tasks/task-work.ts +62 -0
- package/src/tasks/waiting-child.ts +203 -0
- package/src/tasks/wake-parent.ts +142 -0
- package/src/tasks/wiring.ts +231 -0
- package/src/title-file.ts +57 -0
- package/src/usage/accounts.ts +86 -0
- package/src/usage/aggregate.ts +220 -0
- package/src/usage/exchange.ts +93 -0
- package/src/usage/index-file.ts +178 -0
- package/src/usage/limits/monitor.ts +239 -0
- package/src/usage/limits/normalize.ts +188 -0
- package/src/usage/limits/probe.ts +120 -0
- package/src/usage/prices-snapshot.json +1071 -0
- package/src/usage/pricing.ts +236 -0
- package/src/usage/projects.ts +65 -0
- package/src/usage/readers/claude.ts +60 -0
- package/src/usage/readers/codex.ts +196 -0
- package/src/usage/record.ts +60 -0
- package/src/usage/roots.ts +39 -0
- package/src/usage/scanner.ts +265 -0
- package/src/usage/usage-service.ts +161 -0
- package/src/watch-seam.ts +111 -0
- package/src/watch-test-helpers.ts +102 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { open, stat } from 'node:fs/promises';
|
|
2
|
+
// The end of a transcript is all the question needs; a final line longer than this reads as undecided.
|
|
3
|
+
const TAIL_BYTES = 1024 * 1024;
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
/* The report an agent handed back with `SubagentHandback` before its last message, which then only points at it. */
|
|
8
|
+
function handbackBefore(lines, end) {
|
|
9
|
+
for (let i = end - 1; i >= 0; i--) {
|
|
10
|
+
let entry;
|
|
11
|
+
try {
|
|
12
|
+
entry = JSON.parse(lines[i]);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const message = isRecord(entry) && entry.type === 'assistant' && isRecord(entry.message) ? entry.message : null;
|
|
18
|
+
const content = message && Array.isArray(message.content) ? message.content.filter(isRecord) : [];
|
|
19
|
+
for (const block of content) {
|
|
20
|
+
const input = block.type === 'tool_use' && block.name === 'SubagentHandback' && isRecord(block.input) ? block.input : null;
|
|
21
|
+
if (input && typeof input.message === 'string' && input.message.trim() !== '') {
|
|
22
|
+
return input.message;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
/*
|
|
29
|
+
* Whether the end of a Claude subagent transcript shows an agent that finished: the last line that
|
|
30
|
+
* is a message is the assistant's, it ended its turn (`stop_reason: end_turn`) and it asked for no
|
|
31
|
+
* tool. Anything else (a tool call still out, a tool result the model has not answered yet, a line
|
|
32
|
+
* still being written, a file cut off in the middle of a line) is not an answer, and null leaves the
|
|
33
|
+
* row as it was. `text` starts on a line boundary.
|
|
34
|
+
*/
|
|
35
|
+
export function settlementOf(text) {
|
|
36
|
+
if (!text.endsWith('\n')) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const lines = text.split('\n');
|
|
40
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
41
|
+
const line = lines[i].trim();
|
|
42
|
+
if (line === '') {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
let entry;
|
|
46
|
+
try {
|
|
47
|
+
entry = JSON.parse(line);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
if (!isRecord(entry) || (entry.type !== 'user' && entry.type !== 'assistant')) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const message = isRecord(entry.message) ? entry.message : {};
|
|
56
|
+
const content = Array.isArray(message.content) ? message.content.filter(isRecord) : [];
|
|
57
|
+
if (entry.type !== 'assistant' || message.stop_reason !== 'end_turn' || content.some((block) => block.type === 'tool_use')) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const time = typeof entry.timestamp === 'string' ? Date.parse(entry.timestamp) : Number.NaN;
|
|
61
|
+
const handedBack = handbackBefore(lines, i);
|
|
62
|
+
const report = handedBack ??
|
|
63
|
+
content
|
|
64
|
+
.map((block) => (block.type === 'text' && typeof block.text === 'string' ? block.text : ''))
|
|
65
|
+
.filter((part) => part.trim() !== '')
|
|
66
|
+
.join('\n');
|
|
67
|
+
return { finishedAt: Number.isFinite(time) ? time : null, report: report === '' ? null : report };
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
/*
|
|
72
|
+
* Reads the end of a transcript and asks `settlementOf`. The file has to be the same size and age
|
|
73
|
+
* after the read as before it: a transcript that grew in between belongs to an agent still writing.
|
|
74
|
+
*/
|
|
75
|
+
export async function readSubagentSettlement(path) {
|
|
76
|
+
let before;
|
|
77
|
+
try {
|
|
78
|
+
before = await stat(path);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const from = Math.max(0, before.size - TAIL_BYTES);
|
|
84
|
+
const handle = await open(path, 'r');
|
|
85
|
+
let text;
|
|
86
|
+
try {
|
|
87
|
+
const bytes = Buffer.alloc(before.size - from);
|
|
88
|
+
const { bytesRead } = await handle.read(bytes, 0, bytes.length, from);
|
|
89
|
+
text = bytes.subarray(0, bytesRead).toString('utf8');
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
await handle.close();
|
|
93
|
+
}
|
|
94
|
+
const after = await stat(path).catch(() => null);
|
|
95
|
+
if (after === null || after.size !== before.size || after.mtimeMs !== before.mtimeMs) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
if (from > 0) {
|
|
99
|
+
const start = text.indexOf('\n');
|
|
100
|
+
text = start < 0 ? '' : text.slice(start + 1);
|
|
101
|
+
}
|
|
102
|
+
return text === '' ? null : settlementOf(text);
|
|
103
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AgentStatus, ChatApprovalItem, ChatEvent, ChatHistoryResult, ChatInfo, ChatItem, ChatQuestionItem } from '@adecore/agent-contracts';
|
|
2
|
+
export declare class ChatThread {
|
|
3
|
+
info: ChatInfo;
|
|
4
|
+
private readonly items;
|
|
5
|
+
private readonly order;
|
|
6
|
+
private historyGeneration;
|
|
7
|
+
private readonly indices;
|
|
8
|
+
constructor(info: ChatInfo, items?: ChatItem[]);
|
|
9
|
+
get(id: string): ChatItem | undefined;
|
|
10
|
+
list(): ChatItem[];
|
|
11
|
+
find<K extends ChatItem['kind']>(kind: K, match: (item: Extract<ChatItem, {
|
|
12
|
+
kind: K;
|
|
13
|
+
}>) => boolean): Extract<ChatItem, {
|
|
14
|
+
kind: K;
|
|
15
|
+
}> | undefined;
|
|
16
|
+
upsert(item: ChatItem): ChatEvent;
|
|
17
|
+
appendText(itemId: string, text: string): ChatEvent | null;
|
|
18
|
+
patchInfo(patch: Partial<ChatInfo>): ChatEvent;
|
|
19
|
+
reset(patch: Partial<ChatInfo>): ChatEvent;
|
|
20
|
+
apply(event: ChatEvent): void;
|
|
21
|
+
setStatus(status: AgentStatus): ChatEvent;
|
|
22
|
+
statusFor(activeTurnId: string | null): AgentStatus;
|
|
23
|
+
pending(): Array<ChatApprovalItem | ChatQuestionItem>;
|
|
24
|
+
private syncRequests;
|
|
25
|
+
history(limit?: number, cursor?: string): ChatHistoryResult;
|
|
26
|
+
snapshot(): {
|
|
27
|
+
info: ChatInfo;
|
|
28
|
+
items: ChatItem[];
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { ChatError } from './errors.js';
|
|
3
|
+
import { requestSummaries } from './request-summary.js';
|
|
4
|
+
/*
|
|
5
|
+
* The state of one chat as the client sees it. Every mutation answers the event that describes
|
|
6
|
+
* it, so the host broadcasts exactly what it applied and a reload can rebuild from `snapshot`.
|
|
7
|
+
*/
|
|
8
|
+
export class ChatThread {
|
|
9
|
+
info;
|
|
10
|
+
items = new Map();
|
|
11
|
+
order = [];
|
|
12
|
+
historyGeneration = randomUUID();
|
|
13
|
+
indices = new Map();
|
|
14
|
+
constructor(info, items = []) {
|
|
15
|
+
this.info = info;
|
|
16
|
+
for (const item of items) {
|
|
17
|
+
this.items.set(item.id, item);
|
|
18
|
+
this.indices.set(item.id, this.order.length);
|
|
19
|
+
this.order.push(item.id);
|
|
20
|
+
}
|
|
21
|
+
this.syncRequests();
|
|
22
|
+
}
|
|
23
|
+
get(id) {
|
|
24
|
+
return this.items.get(id);
|
|
25
|
+
}
|
|
26
|
+
list() {
|
|
27
|
+
return this.order.map((id) => this.items.get(id));
|
|
28
|
+
}
|
|
29
|
+
find(kind, match) {
|
|
30
|
+
for (let i = this.order.length - 1; i >= 0; i--) {
|
|
31
|
+
const item = this.items.get(this.order[i]);
|
|
32
|
+
if (item?.kind === kind && match(item)) {
|
|
33
|
+
return item;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
upsert(item) {
|
|
39
|
+
if (!this.items.has(item.id)) {
|
|
40
|
+
this.indices.set(item.id, this.order.length);
|
|
41
|
+
this.order.push(item.id);
|
|
42
|
+
}
|
|
43
|
+
this.items.set(item.id, item);
|
|
44
|
+
if (item.kind === 'approval' || item.kind === 'question') {
|
|
45
|
+
this.syncRequests();
|
|
46
|
+
}
|
|
47
|
+
return { type: 'item', item, historyIndex: this.indices.get(item.id) };
|
|
48
|
+
}
|
|
49
|
+
/* Streams text into an assistant or thinking item, or partial output into a tool call that is still running. */
|
|
50
|
+
appendText(itemId, text) {
|
|
51
|
+
const item = this.items.get(itemId);
|
|
52
|
+
if (item?.kind === 'assistant' || item?.kind === 'thinking') {
|
|
53
|
+
this.items.set(itemId, { ...item, text: item.text + text });
|
|
54
|
+
return { type: 'delta', itemId, text };
|
|
55
|
+
}
|
|
56
|
+
if (item?.kind === 'tool' && item.state === 'running') {
|
|
57
|
+
const progress = item.progress ?? { startedAt: null, description: null, output: null };
|
|
58
|
+
this.items.set(itemId, { ...item, progress: { ...progress, output: (progress.output ?? '') + text } });
|
|
59
|
+
return { type: 'delta', itemId, text };
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
patchInfo(patch) {
|
|
64
|
+
this.info = { ...this.info, ...patch };
|
|
65
|
+
return { type: 'info', info: this.info };
|
|
66
|
+
}
|
|
67
|
+
/* Empties the thread; the info carries on with the patch on top. */
|
|
68
|
+
reset(patch) {
|
|
69
|
+
this.items.clear();
|
|
70
|
+
this.indices.clear();
|
|
71
|
+
this.historyGeneration = randomUUID();
|
|
72
|
+
this.order.length = 0;
|
|
73
|
+
this.info = { ...this.info, ...patch };
|
|
74
|
+
this.syncRequests();
|
|
75
|
+
return { type: 'reset', info: this.info, items: [] };
|
|
76
|
+
}
|
|
77
|
+
/* Applies an event this thread answered before, as a log read back after a restart hands it in. */
|
|
78
|
+
apply(event) {
|
|
79
|
+
switch (event.type) {
|
|
80
|
+
case 'item':
|
|
81
|
+
this.upsert(event.item);
|
|
82
|
+
break;
|
|
83
|
+
case 'delta':
|
|
84
|
+
this.appendText(event.itemId, event.text);
|
|
85
|
+
break;
|
|
86
|
+
case 'info':
|
|
87
|
+
this.info = event.info;
|
|
88
|
+
break;
|
|
89
|
+
case 'reset':
|
|
90
|
+
this.reset(event.info);
|
|
91
|
+
for (const item of event.items) {
|
|
92
|
+
this.upsert(item);
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
setStatus(status) {
|
|
98
|
+
return this.patchInfo({ status });
|
|
99
|
+
}
|
|
100
|
+
/* A request can outlive the turn it came in, when work the CLI runs beside its turns asked it. */
|
|
101
|
+
statusFor(activeTurnId) {
|
|
102
|
+
if (this.pending().length > 0) {
|
|
103
|
+
return 'needs-you';
|
|
104
|
+
}
|
|
105
|
+
return activeTurnId === null ? 'idle' : 'running';
|
|
106
|
+
}
|
|
107
|
+
pending() {
|
|
108
|
+
return this.list().filter((item) => (item.kind === 'approval' && item.decision === 'pending') || (item.kind === 'question' && item.state === 'pending'));
|
|
109
|
+
}
|
|
110
|
+
/*
|
|
111
|
+
* `info.requests` follows the request items, so whatever event comes next carries what the chat waits on,
|
|
112
|
+
* and a log read back rebuilds it the same way.
|
|
113
|
+
*/
|
|
114
|
+
syncRequests() {
|
|
115
|
+
const { requests: _requests, ...rest } = this.info;
|
|
116
|
+
const pending = this.pending();
|
|
117
|
+
this.info = pending.length === 0 ? rest : { ...rest, requests: requestSummaries(pending) };
|
|
118
|
+
}
|
|
119
|
+
history(limit = 60, cursor) {
|
|
120
|
+
let end = this.order.length;
|
|
121
|
+
if (cursor) {
|
|
122
|
+
const [generation, offset] = cursor.split(':');
|
|
123
|
+
end = Number(offset);
|
|
124
|
+
if (generation !== this.historyGeneration || !/^\d+$/.test(offset ?? '') || !Number.isSafeInteger(end) || end < 0 || end > this.order.length) {
|
|
125
|
+
throw new ChatError('history-expired', 'The conversation changed. Reload its history.');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const items = [];
|
|
129
|
+
let bytes = 0;
|
|
130
|
+
let start = end;
|
|
131
|
+
while (start > 0 && items.length < Math.min(100, Math.max(1, limit))) {
|
|
132
|
+
const item = this.items.get(this.order[start - 1]);
|
|
133
|
+
const size = Buffer.byteLength(JSON.stringify(item));
|
|
134
|
+
// An atomic item is never cut; an oversized item travels alone.
|
|
135
|
+
if (items.length && bytes + size > 512 * 1024) {
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
items.unshift(item);
|
|
139
|
+
bytes += size;
|
|
140
|
+
start--;
|
|
141
|
+
}
|
|
142
|
+
return { items, history: { start, cursor: start ? `${this.historyGeneration}:${start}` : null } };
|
|
143
|
+
}
|
|
144
|
+
snapshot() {
|
|
145
|
+
return { info: this.info, items: this.list() };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ChatItem } from '@adecore/agent-contracts';
|
|
2
|
+
import type { ChatCore } from './chat-core.ts';
|
|
3
|
+
import type { ChatSession } from './chat-session.ts';
|
|
4
|
+
export interface WakeChat {
|
|
5
|
+
items(): ChatItem[];
|
|
6
|
+
persist?(): Promise<void>;
|
|
7
|
+
wake(wake: {
|
|
8
|
+
text: string;
|
|
9
|
+
label: string;
|
|
10
|
+
note?: string;
|
|
11
|
+
taskIds: string[];
|
|
12
|
+
messageFrom?: string[];
|
|
13
|
+
}): boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface ChatOpenerDeps {
|
|
16
|
+
chats: Pick<ChatCore, 'get' | 'hasStored' | 'create' | 'save'>;
|
|
17
|
+
placed(nodeId: string): boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function loadChat(deps: ChatOpenerDeps, chatId: string): Promise<ChatSession | null>;
|
|
20
|
+
export declare function chatOpener(deps: ChatOpenerDeps): (chatId: string) => Promise<WakeChat | null>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* A chat, loaded from disk when nobody has it, so work owed to a chat nobody mounted since the restart
|
|
3
|
+
* still lands. Null for a node with no thread at all.
|
|
4
|
+
*/
|
|
5
|
+
export async function loadChat(deps, chatId) {
|
|
6
|
+
if (!deps.chats.get(chatId)) {
|
|
7
|
+
if (!deps.placed(chatId) || !(await deps.chats.hasStored(chatId))) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
await deps.chats.create({ chatId });
|
|
11
|
+
}
|
|
12
|
+
return deps.chats.get(chatId) ?? null;
|
|
13
|
+
}
|
|
14
|
+
/* The chat a turn is to be opened in. */
|
|
15
|
+
export function chatOpener(deps) {
|
|
16
|
+
return async (chatId) => {
|
|
17
|
+
const session = await loadChat(deps, chatId);
|
|
18
|
+
return session
|
|
19
|
+
? {
|
|
20
|
+
items: () => session.thread.list(),
|
|
21
|
+
persist: () => deps.chats.save(chatId),
|
|
22
|
+
// A turn before the reset would stop on the same limit and spend one of the resume's tries, so it waits for the resume turn.
|
|
23
|
+
wake: (wake) => session.info.resumeAt === undefined && session.wake(wake) !== null
|
|
24
|
+
}
|
|
25
|
+
: null;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class ClientSinks<TEvent> {
|
|
2
|
+
private readonly sinks;
|
|
3
|
+
private readonly onRelease;
|
|
4
|
+
constructor(onRelease?: ((clientId: string) => void) | null);
|
|
5
|
+
subscribe(clientId: string, sink: (event: TEvent) => void): () => void;
|
|
6
|
+
emit(event: TEvent): void;
|
|
7
|
+
to(clientId: string, event: TEvent): void;
|
|
8
|
+
clientIds(): string[];
|
|
9
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The clients listening to one part of a host. A client is one sink at a time; a client that
|
|
3
|
+
* connects again replaces the one before it.
|
|
4
|
+
*/
|
|
5
|
+
export class ClientSinks {
|
|
6
|
+
sinks = new Map();
|
|
7
|
+
onRelease;
|
|
8
|
+
/* What else a client leaves behind, for the parts that keep more than a sink per client. */
|
|
9
|
+
constructor(onRelease = null) {
|
|
10
|
+
this.onRelease = onRelease;
|
|
11
|
+
}
|
|
12
|
+
subscribe(clientId, sink) {
|
|
13
|
+
this.sinks.set(clientId, sink);
|
|
14
|
+
return () => {
|
|
15
|
+
// A client that subscribes again before it unsubscribes keeps its newer sink.
|
|
16
|
+
if (this.sinks.get(clientId) !== sink) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
this.sinks.delete(clientId);
|
|
20
|
+
this.onRelease?.(clientId);
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
emit(event) {
|
|
24
|
+
for (const sink of this.sinks.values()) {
|
|
25
|
+
sink(event);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
to(clientId, event) {
|
|
29
|
+
this.sinks.get(clientId)?.(event);
|
|
30
|
+
}
|
|
31
|
+
clientIds() {
|
|
32
|
+
return [...this.sinks.keys()];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The shape every failure on the wire has: a code a client can branch on and a message a person reads.
|
|
3
|
+
* A domain gives it a union of its own codes, so a typo in a code is a compile error instead of a
|
|
4
|
+
* string the other end never matches.
|
|
5
|
+
*/
|
|
6
|
+
export class CodedError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
/* What the actor can do instead. Only a refusal fills this; the rest carry an empty list. */
|
|
9
|
+
lines;
|
|
10
|
+
constructor(code, message, lines = []) {
|
|
11
|
+
super(message);
|
|
12
|
+
// The subclass names itself, so a stack reads `GitError:` and not `Error:`.
|
|
13
|
+
this.name = new.target.name;
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.lines = lines;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type ParsedArgv = {
|
|
2
|
+
ok: true;
|
|
3
|
+
positionals: string[];
|
|
4
|
+
flags: Record<string, string>;
|
|
5
|
+
switches: Set<string>;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
code: 'unknown-flag' | 'missing-value' | 'duplicate-flag' | 'unexpected-value';
|
|
9
|
+
message: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function parseArgv(argv: readonly string[], known: readonly string[], switches?: readonly string[]): ParsedArgv;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Splits the words after a verb into positionals, `--flag value` pairs (`--flag=value` too, for a
|
|
3
|
+
* value that itself starts with `--`) and the switches the verb declared. Anything not declared a
|
|
4
|
+
* switch takes a value, so a flag that lost its argument is refused instead of swallowing the next
|
|
5
|
+
* word, and a flag given twice is refused rather than one of the two silently winning. A name that is
|
|
6
|
+
* both a switch and a flag is on when written bare and takes a value only after `=` (`--state=full`).
|
|
7
|
+
*/
|
|
8
|
+
export function parseArgv(argv, known, switches = []) {
|
|
9
|
+
const positionals = [];
|
|
10
|
+
const flags = {};
|
|
11
|
+
const given = new Set();
|
|
12
|
+
const all = [...new Set([...known, ...switches])];
|
|
13
|
+
for (let i = 0; i < argv.length; i++) {
|
|
14
|
+
const word = argv[i];
|
|
15
|
+
if (!word.startsWith('--')) {
|
|
16
|
+
positionals.push(word);
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const equals = word.indexOf('=');
|
|
20
|
+
const name = word.slice(2, equals === -1 ? undefined : equals);
|
|
21
|
+
if (!all.includes(name)) {
|
|
22
|
+
return {
|
|
23
|
+
ok: false,
|
|
24
|
+
code: 'unknown-flag',
|
|
25
|
+
message: all.length === 0 ? `--${name} is not a flag here; this verb takes none` : `--${name} is not one of --${all.join(', --')}`
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (Object.hasOwn(flags, name) || given.has(name)) {
|
|
29
|
+
return { ok: false, code: 'duplicate-flag', message: `--${name} is given twice` };
|
|
30
|
+
}
|
|
31
|
+
if (switches.includes(name)) {
|
|
32
|
+
if (equals !== -1 && known.includes(name)) {
|
|
33
|
+
flags[name] = word.slice(equals + 1);
|
|
34
|
+
given.add(name);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (equals !== -1) {
|
|
38
|
+
return { ok: false, code: 'unexpected-value', message: `--${name} takes no value; it is on when you write it and off when you do not` };
|
|
39
|
+
}
|
|
40
|
+
given.add(name);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
let value;
|
|
44
|
+
if (equals !== -1) {
|
|
45
|
+
value = word.slice(equals + 1);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const next = argv[i + 1];
|
|
49
|
+
if (next === undefined || next.startsWith('--')) {
|
|
50
|
+
return { ok: false, code: 'missing-value', message: `--${name} needs a value (write --${name}=<value> for one that starts with --)` };
|
|
51
|
+
}
|
|
52
|
+
value = next;
|
|
53
|
+
i++;
|
|
54
|
+
}
|
|
55
|
+
flags[name] = value;
|
|
56
|
+
}
|
|
57
|
+
return { ok: true, positionals, flags, switches: given };
|
|
58
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function field(value: string): string;
|
|
2
|
+
export declare function refusalBody(code: string, message: string, lines?: readonly string[]): string;
|
|
3
|
+
export declare function refusalRows(code: string, message: string, lines?: readonly string[]): string;
|
|
4
|
+
export interface ParsedRefusal {
|
|
5
|
+
code: string;
|
|
6
|
+
message: string;
|
|
7
|
+
lines: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function parseRefusalBody(text: string): ParsedRefusal | null;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* How every no reads: `refused`, the code a script branches on and the sentence a model reads, and
|
|
3
|
+
* under it a line per thing the actor can do instead. One row per line, and a line is a row of
|
|
4
|
+
* tab-separated fields of its own, so only a newline is taken out of it: that would split the row in
|
|
5
|
+
* two, and everything under the first row is read as advice.
|
|
6
|
+
*/
|
|
7
|
+
/* A field that goes into a tab-separated line; a tab or a newline in a title would split the row. */
|
|
8
|
+
export function field(value) {
|
|
9
|
+
return value.replace(/[\t\r\n]+/g, ' ');
|
|
10
|
+
}
|
|
11
|
+
function row(line) {
|
|
12
|
+
return line.replace(/[\r\n]+/g, ' ');
|
|
13
|
+
}
|
|
14
|
+
function rows(code, message, lines) {
|
|
15
|
+
return [`${field(code)}\t${field(message)}`, ...lines.map(row)];
|
|
16
|
+
}
|
|
17
|
+
/* What a verb is refused with, whole: the `refused` row and what to do instead under it. */
|
|
18
|
+
export function refusalBody(code, message, lines = []) {
|
|
19
|
+
const [first, ...rest] = rows(code, message, lines);
|
|
20
|
+
return [`refused\t${first}`, ...rest].join('\n');
|
|
21
|
+
}
|
|
22
|
+
/*
|
|
23
|
+
* The same without the word that names it, for a host that adds the prefix in its CLI:
|
|
24
|
+
* The context CLI puts the prefix on, so a CLI of an older build still prints one refusal.
|
|
25
|
+
*/
|
|
26
|
+
export function refusalRows(code, message, lines = []) {
|
|
27
|
+
return rows(code, message, lines).join('\n');
|
|
28
|
+
}
|
|
29
|
+
/* Reads either form back. Null when the text is no refusal at all, which is not the same as one without advice. */
|
|
30
|
+
export function parseRefusalBody(text) {
|
|
31
|
+
const [first = '', ...lines] = text.split('\n').filter((line) => line.trim() !== '');
|
|
32
|
+
const parts = first.split('\t');
|
|
33
|
+
const [code, message] = parts[0] === 'refused' ? parts.slice(1) : parts;
|
|
34
|
+
if (!code || !message) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return { code, message, lines };
|
|
38
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CodedError } from '../coded-error.ts';
|
|
3
|
+
export declare class VerbRefusal extends CodedError {
|
|
4
|
+
}
|
|
5
|
+
export interface VerbCallBase {
|
|
6
|
+
expectedRevision?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface VerbHelp {
|
|
9
|
+
name: string;
|
|
10
|
+
usage: string;
|
|
11
|
+
summary: string;
|
|
12
|
+
detail: readonly string[];
|
|
13
|
+
}
|
|
14
|
+
export interface Verb<Call extends VerbCallBase> extends VerbHelp {
|
|
15
|
+
served: 'verb';
|
|
16
|
+
flagNames: readonly string[];
|
|
17
|
+
dryRun: boolean;
|
|
18
|
+
run(argv: readonly string[], call: Call): Promise<string[]>;
|
|
19
|
+
}
|
|
20
|
+
export interface Action<Call extends VerbCallBase> extends Omit<Verb<Call>, 'served'> {
|
|
21
|
+
word: string;
|
|
22
|
+
}
|
|
23
|
+
export interface Noun<Call extends VerbCallBase> {
|
|
24
|
+
served: 'noun';
|
|
25
|
+
name: string;
|
|
26
|
+
usage: string;
|
|
27
|
+
summary: string;
|
|
28
|
+
detail: readonly string[];
|
|
29
|
+
actions: readonly Action<Call>[];
|
|
30
|
+
run(argv: readonly string[], call: Call): Promise<string[]>;
|
|
31
|
+
}
|
|
32
|
+
export interface ContextVerb extends VerbHelp {
|
|
33
|
+
served: 'context';
|
|
34
|
+
}
|
|
35
|
+
export type VerbEntry<Call extends VerbCallBase> = Verb<Call> | Noun<Call> | ContextVerb;
|
|
36
|
+
export declare const DRY_RUN_FLAG = "dry-run";
|
|
37
|
+
export declare const REVISION_FLAG = "revision";
|
|
38
|
+
export declare const DRY_RUN_PREVIEW = "a refused call makes nothing either, so it is only a preview and never needed for safety";
|
|
39
|
+
export interface ArgsSpec<Positionals extends z.ZodType, Flags extends z.ZodObject, Call extends VerbCallBase> {
|
|
40
|
+
positionals: Positionals;
|
|
41
|
+
flags: Flags;
|
|
42
|
+
switches?: readonly string[];
|
|
43
|
+
dryRun?: boolean;
|
|
44
|
+
revision?: boolean;
|
|
45
|
+
run(input: {
|
|
46
|
+
positionals: z.infer<Positionals>;
|
|
47
|
+
flags: z.infer<Flags>;
|
|
48
|
+
switches: ReadonlySet<string>;
|
|
49
|
+
dryRun: boolean;
|
|
50
|
+
}, call: Call): Promise<string[]>;
|
|
51
|
+
}
|
|
52
|
+
export interface VerbSpec<Positionals extends z.ZodType, Flags extends z.ZodObject, Call extends VerbCallBase> extends VerbHelp, ArgsSpec<Positionals, Flags, Call> {
|
|
53
|
+
}
|
|
54
|
+
export interface NounSpec<Call extends VerbCallBase> {
|
|
55
|
+
name: string;
|
|
56
|
+
summary: string;
|
|
57
|
+
detail: readonly string[];
|
|
58
|
+
actions: readonly Action<Call>[];
|
|
59
|
+
}
|
|
60
|
+
export interface HelpTopic {
|
|
61
|
+
name: string;
|
|
62
|
+
summary: string;
|
|
63
|
+
body: readonly string[];
|
|
64
|
+
}
|
|
65
|
+
export interface HelpSpec<Call extends VerbCallBase> {
|
|
66
|
+
entries(): readonly VerbEntry<Call>[];
|
|
67
|
+
root(): readonly string[];
|
|
68
|
+
refusal: string;
|
|
69
|
+
topics?: readonly HelpTopic[];
|
|
70
|
+
}
|
|
71
|
+
export interface VerbRegistry<Call extends VerbCallBase> {
|
|
72
|
+
defineVerb<Positionals extends z.ZodType, Flags extends z.ZodObject>(spec: VerbSpec<Positionals, Flags, Call>): Verb<Call>;
|
|
73
|
+
defineAction<Positionals extends z.ZodType, Flags extends z.ZodObject>(noun: string, spec: VerbSpec<Positionals, Flags, Call>): Action<Call>;
|
|
74
|
+
defineNoun(spec: NounSpec<Call>): Noun<Call>;
|
|
75
|
+
defineHelp(spec: HelpSpec<Call>): Verb<Call>;
|
|
76
|
+
dryRunVerbNames(): readonly string[];
|
|
77
|
+
dryRunLine(): string;
|
|
78
|
+
}
|
|
79
|
+
export declare function summaryLines<Call extends VerbCallBase>(entries: readonly VerbEntry<Call>[]): string[];
|
|
80
|
+
export declare function createVerbRegistry<Call extends VerbCallBase>(options: {
|
|
81
|
+
cli: string;
|
|
82
|
+
}): VerbRegistry<Call>;
|
|
83
|
+
export declare function lengthOf(input: unknown): number;
|
|
84
|
+
export declare function requiredField(needs: string): z.ZodString;
|
|
85
|
+
export declare function orNote(lines: string[], note: string): string[];
|