@chengchenccc/oh-my-agent 0.1.1-rc.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/README.md +79 -0
- package/dist/cli/args.d.ts +24 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +120 -0
- package/dist/cli/initial-input.d.ts +32 -0
- package/dist/cli/initial-input.d.ts.map +1 -0
- package/dist/cli/initial-input.js +131 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +3 -0
- package/dist/core/__fixtures__/echo-model.d.ts +6 -0
- package/dist/core/__fixtures__/echo-model.d.ts.map +1 -0
- package/dist/core/__fixtures__/echo-model.js +10 -0
- package/dist/core/__fixtures__/mcp-echo-server.d.ts +2 -0
- package/dist/core/__fixtures__/mcp-echo-server.d.ts.map +1 -0
- package/dist/core/__fixtures__/mcp-echo-server.js +54 -0
- package/dist/core/agent-runtime.d.ts +21 -0
- package/dist/core/agent-runtime.d.ts.map +1 -0
- package/dist/core/agent-runtime.js +14 -0
- package/dist/core/memory/autonomous-memory.d.ts +21 -0
- package/dist/core/memory/autonomous-memory.d.ts.map +1 -0
- package/dist/core/memory/autonomous-memory.js +202 -0
- package/dist/core/persistence/in-memory-session-store.d.ts +3 -0
- package/dist/core/persistence/in-memory-session-store.d.ts.map +1 -0
- package/dist/core/persistence/in-memory-session-store.js +109 -0
- package/dist/core/persistence/session-store.contract.d.ts +5 -0
- package/dist/core/persistence/session-store.contract.d.ts.map +1 -0
- package/dist/core/persistence/session-store.contract.js +183 -0
- package/dist/core/persistence/session-store.d.ts +33 -0
- package/dist/core/persistence/session-store.d.ts.map +1 -0
- package/dist/core/persistence/session-store.js +22 -0
- package/dist/core/persistence/session-tree.d.ts +49 -0
- package/dist/core/persistence/session-tree.d.ts.map +1 -0
- package/dist/core/persistence/session-tree.js +1 -0
- package/dist/core/plugins/plugin-code.d.ts +13 -0
- package/dist/core/plugins/plugin-code.d.ts.map +1 -0
- package/dist/core/plugins/plugin-code.js +62 -0
- package/dist/core/plugins/plugin-marketplace.d.ts +72 -0
- package/dist/core/plugins/plugin-marketplace.d.ts.map +1 -0
- package/dist/core/plugins/plugin-marketplace.js +340 -0
- package/dist/core/plugins/plugin-resolve.d.ts +32 -0
- package/dist/core/plugins/plugin-resolve.d.ts.map +1 -0
- package/dist/core/plugins/plugin-resolve.js +101 -0
- package/dist/core/plugins/plugin-trust.d.ts +24 -0
- package/dist/core/plugins/plugin-trust.d.ts.map +1 -0
- package/dist/core/plugins/plugin-trust.js +89 -0
- package/dist/core/runtime/agent-event.d.ts +98 -0
- package/dist/core/runtime/agent-event.d.ts.map +1 -0
- package/dist/core/runtime/agent-event.js +1 -0
- package/dist/core/runtime/agent-loop.d.ts +142 -0
- package/dist/core/runtime/agent-loop.d.ts.map +1 -0
- package/dist/core/runtime/agent-loop.js +1008 -0
- package/dist/core/runtime/approval.d.ts +23 -0
- package/dist/core/runtime/approval.d.ts.map +1 -0
- package/dist/core/runtime/approval.js +25 -0
- package/dist/core/runtime/compaction.d.ts +22 -0
- package/dist/core/runtime/compaction.d.ts.map +1 -0
- package/dist/core/runtime/compaction.js +90 -0
- package/dist/core/runtime/context-estimate.d.ts +42 -0
- package/dist/core/runtime/context-estimate.d.ts.map +1 -0
- package/dist/core/runtime/context-estimate.js +34 -0
- package/dist/core/runtime/create-runtime.d.ts +81 -0
- package/dist/core/runtime/create-runtime.d.ts.map +1 -0
- package/dist/core/runtime/create-runtime.js +250 -0
- package/dist/core/runtime/fake-provider.d.ts +3 -0
- package/dist/core/runtime/fake-provider.d.ts.map +1 -0
- package/dist/core/runtime/fake-provider.js +87 -0
- package/dist/core/runtime/logger.d.ts +13 -0
- package/dist/core/runtime/logger.d.ts.map +1 -0
- package/dist/core/runtime/logger.js +38 -0
- package/dist/core/runtime/loop-input.d.ts +39 -0
- package/dist/core/runtime/loop-input.d.ts.map +1 -0
- package/dist/core/runtime/loop-input.js +38 -0
- package/dist/core/runtime/message-cache.d.ts +16 -0
- package/dist/core/runtime/message-cache.d.ts.map +1 -0
- package/dist/core/runtime/message-cache.js +23 -0
- package/dist/core/runtime/model-catalog.d.ts +10 -0
- package/dist/core/runtime/model-catalog.d.ts.map +1 -0
- package/dist/core/runtime/model-catalog.js +14 -0
- package/dist/core/runtime/plugin-runtime.d.ts +41 -0
- package/dist/core/runtime/plugin-runtime.d.ts.map +1 -0
- package/dist/core/runtime/plugin-runtime.js +1 -0
- package/dist/core/runtime/plugin.d.ts +86 -0
- package/dist/core/runtime/plugin.d.ts.map +1 -0
- package/dist/core/runtime/plugin.js +23 -0
- package/dist/core/runtime/process-tree.d.ts +12 -0
- package/dist/core/runtime/process-tree.d.ts.map +1 -0
- package/dist/core/runtime/process-tree.js +40 -0
- package/dist/core/runtime/prompt.d.ts +23 -0
- package/dist/core/runtime/prompt.d.ts.map +1 -0
- package/dist/core/runtime/prompt.js +29 -0
- package/dist/core/runtime/prompts.d.ts +16 -0
- package/dist/core/runtime/prompts.d.ts.map +1 -0
- package/dist/core/runtime/prompts.js +36 -0
- package/dist/core/runtime/retry.d.ts +20 -0
- package/dist/core/runtime/retry.d.ts.map +1 -0
- package/dist/core/runtime/retry.js +60 -0
- package/dist/core/runtime/run-runtime.d.ts +93 -0
- package/dist/core/runtime/run-runtime.d.ts.map +1 -0
- package/dist/core/runtime/run-runtime.js +664 -0
- package/dist/core/runtime/runtime-catalog.d.ts +12 -0
- package/dist/core/runtime/runtime-catalog.d.ts.map +1 -0
- package/dist/core/runtime/runtime-catalog.js +89 -0
- package/dist/core/runtime/stream-rules.d.ts +9 -0
- package/dist/core/runtime/stream-rules.d.ts.map +1 -0
- package/dist/core/runtime/stream-rules.js +44 -0
- package/dist/core/runtime/title.d.ts +7 -0
- package/dist/core/runtime/title.d.ts.map +1 -0
- package/dist/core/runtime/title.js +114 -0
- package/dist/core/runtime/todo.d.ts +16 -0
- package/dist/core/runtime/todo.d.ts.map +1 -0
- package/dist/core/runtime/todo.js +40 -0
- package/dist/core/runtime/tool-filter.d.ts +12 -0
- package/dist/core/runtime/tool-filter.d.ts.map +1 -0
- package/dist/core/runtime/tool-filter.js +23 -0
- package/dist/core/runtime/tool-pruning.d.ts +29 -0
- package/dist/core/runtime/tool-pruning.d.ts.map +1 -0
- package/dist/core/runtime/tool-pruning.js +105 -0
- package/dist/core/session/input-history.d.ts +8 -0
- package/dist/core/session/input-history.d.ts.map +1 -0
- package/dist/core/session/input-history.js +41 -0
- package/dist/core/session/session-file.d.ts +84 -0
- package/dist/core/session/session-file.d.ts.map +1 -0
- package/dist/core/session/session-file.js +392 -0
- package/dist/core/session/session-loop.d.ts +33 -0
- package/dist/core/session/session-loop.d.ts.map +1 -0
- package/dist/core/session/session-loop.js +30 -0
- package/dist/core/settings/project-settings.d.ts +46 -0
- package/dist/core/settings/project-settings.d.ts.map +1 -0
- package/dist/core/settings/project-settings.js +85 -0
- package/dist/core/settings/workspace-context.d.ts +10 -0
- package/dist/core/settings/workspace-context.d.ts.map +1 -0
- package/dist/core/settings/workspace-context.js +48 -0
- package/dist/core/tools/agent-fs-like.d.ts +15 -0
- package/dist/core/tools/agent-fs-like.d.ts.map +1 -0
- package/dist/core/tools/agent-fs-like.js +4 -0
- package/dist/core/tools/ask-question.d.ts +5 -0
- package/dist/core/tools/ask-question.d.ts.map +1 -0
- package/dist/core/tools/ask-question.js +51 -0
- package/dist/core/tools/bash.d.ts +5 -0
- package/dist/core/tools/bash.d.ts.map +1 -0
- package/dist/core/tools/bash.js +109 -0
- package/dist/core/tools/eval.d.ts +10 -0
- package/dist/core/tools/eval.d.ts.map +1 -0
- package/dist/core/tools/eval.js +83 -0
- package/dist/core/tools/file-tools.d.ts +18 -0
- package/dist/core/tools/file-tools.d.ts.map +1 -0
- package/dist/core/tools/file-tools.js +249 -0
- package/dist/core/tools/glob.d.ts +5 -0
- package/dist/core/tools/glob.d.ts.map +1 -0
- package/dist/core/tools/glob.js +54 -0
- package/dist/core/tools/grep.d.ts +5 -0
- package/dist/core/tools/grep.d.ts.map +1 -0
- package/dist/core/tools/grep.js +61 -0
- package/dist/core/tools/index.d.ts +14 -0
- package/dist/core/tools/index.d.ts.map +1 -0
- package/dist/core/tools/index.js +14 -0
- package/dist/core/tools/ls-tree.d.ts +10 -0
- package/dist/core/tools/ls-tree.d.ts.map +1 -0
- package/dist/core/tools/ls-tree.js +177 -0
- package/dist/core/tools/mcp-mount.d.ts +63 -0
- package/dist/core/tools/mcp-mount.d.ts.map +1 -0
- package/dist/core/tools/mcp-mount.js +287 -0
- package/dist/core/tools/read-image.d.ts +6 -0
- package/dist/core/tools/read-image.d.ts.map +1 -0
- package/dist/core/tools/read-image.js +98 -0
- package/dist/core/tools/skill.d.ts +11 -0
- package/dist/core/tools/skill.d.ts.map +1 -0
- package/dist/core/tools/skill.js +65 -0
- package/dist/core/tools/skills.d.ts +10 -0
- package/dist/core/tools/skills.d.ts.map +1 -0
- package/dist/core/tools/skills.js +116 -0
- package/dist/core/tools/todo-store.d.ts +8 -0
- package/dist/core/tools/todo-store.d.ts.map +1 -0
- package/dist/core/tools/todo-store.js +100 -0
- package/dist/core/tools/todo.d.ts +8 -0
- package/dist/core/tools/todo.d.ts.map +1 -0
- package/dist/core/tools/todo.js +91 -0
- package/dist/core/tools/url-guard.d.ts +12 -0
- package/dist/core/tools/url-guard.d.ts.map +1 -0
- package/dist/core/tools/url-guard.js +58 -0
- package/dist/core/tools/web-ports-std.d.ts +6 -0
- package/dist/core/tools/web-ports-std.d.ts.map +1 -0
- package/dist/core/tools/web-ports-std.js +145 -0
- package/dist/core/tools/web-ports.d.ts +56 -0
- package/dist/core/tools/web-ports.d.ts.map +1 -0
- package/dist/core/tools/web-ports.js +62 -0
- package/dist/core/tools/workspace-sandbox.d.ts +20 -0
- package/dist/core/tools/workspace-sandbox.d.ts.map +1 -0
- package/dist/core/tools/workspace-sandbox.js +84 -0
- package/dist/core/workflow/subagent-registry.d.ts +40 -0
- package/dist/core/workflow/subagent-registry.d.ts.map +1 -0
- package/dist/core/workflow/subagent-registry.js +118 -0
- package/dist/core/workflow/workflow-evaluator.d.ts +23 -0
- package/dist/core/workflow/workflow-evaluator.d.ts.map +1 -0
- package/dist/core/workflow/workflow-evaluator.js +21 -0
- package/dist/core/workflow/workflow-executor.d.ts +108 -0
- package/dist/core/workflow/workflow-executor.d.ts.map +1 -0
- package/dist/core/workflow/workflow-executor.js +549 -0
- package/dist/core/workflow/workflow-tools.d.ts +50 -0
- package/dist/core/workflow/workflow-tools.d.ts.map +1 -0
- package/dist/core/workflow/workflow-tools.js +217 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/main.d.ts +9 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +97 -0
- package/dist/modes/json-mode.d.ts +5 -0
- package/dist/modes/json-mode.d.ts.map +1 -0
- package/dist/modes/json-mode.js +46 -0
- package/dist/modes/print-mode.d.ts +20 -0
- package/dist/modes/print-mode.d.ts.map +1 -0
- package/dist/modes/print-mode.js +82 -0
- package/dist/modes/rpc/jsonl.d.ts +23 -0
- package/dist/modes/rpc/jsonl.d.ts.map +1 -0
- package/dist/modes/rpc/jsonl.js +88 -0
- package/dist/modes/rpc/rpc-mode.d.ts +18 -0
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-mode.js +326 -0
- package/dist/modes/tui/settings-overlay.d.ts +23 -0
- package/dist/modes/tui/settings-overlay.d.ts.map +1 -0
- package/dist/modes/tui/settings-overlay.js +136 -0
- package/dist/modes/tui/tui-commands-session.d.ts +3 -0
- package/dist/modes/tui/tui-commands-session.d.ts.map +1 -0
- package/dist/modes/tui/tui-commands-session.js +191 -0
- package/dist/modes/tui/tui-commands.d.ts +52 -0
- package/dist/modes/tui/tui-commands.d.ts.map +1 -0
- package/dist/modes/tui/tui-commands.js +373 -0
- package/dist/modes/tui/tui-components.d.ts +81 -0
- package/dist/modes/tui/tui-components.d.ts.map +1 -0
- package/dist/modes/tui/tui-components.js +248 -0
- package/dist/modes/tui/tui-format.d.ts +65 -0
- package/dist/modes/tui/tui-format.d.ts.map +1 -0
- package/dist/modes/tui/tui-format.js +246 -0
- package/dist/modes/tui/tui-frame-provider.d.ts +16 -0
- package/dist/modes/tui/tui-frame-provider.d.ts.map +1 -0
- package/dist/modes/tui/tui-frame-provider.js +31 -0
- package/dist/modes/tui/tui-interactive.d.ts +16 -0
- package/dist/modes/tui/tui-interactive.d.ts.map +1 -0
- package/dist/modes/tui/tui-interactive.js +123 -0
- package/dist/modes/tui/tui-io.d.ts +12 -0
- package/dist/modes/tui/tui-io.d.ts.map +1 -0
- package/dist/modes/tui/tui-io.js +620 -0
- package/dist/modes/tui/tui-mode.d.ts +120 -0
- package/dist/modes/tui/tui-mode.d.ts.map +1 -0
- package/dist/modes/tui/tui-mode.js +391 -0
- package/dist/modes/tui/tui-render.d.ts +54 -0
- package/dist/modes/tui/tui-render.d.ts.map +1 -0
- package/dist/modes/tui/tui-render.js +399 -0
- package/dist/modes/tui/tui-slash.d.ts +12 -0
- package/dist/modes/tui/tui-slash.d.ts.map +1 -0
- package/dist/modes/tui/tui-slash.js +64 -0
- package/dist/modes/tui/tui-tool-render.d.ts +6 -0
- package/dist/modes/tui/tui-tool-render.d.ts.map +1 -0
- package/dist/modes/tui/tui-tool-render.js +79 -0
- package/dist/modes/tui/tui-transcript-reconciler.d.ts +25 -0
- package/dist/modes/tui/tui-transcript-reconciler.d.ts.map +1 -0
- package/dist/modes/tui/tui-transcript-reconciler.js +97 -0
- package/dist/modes/tui/view-state.d.ts +67 -0
- package/dist/modes/tui/view-state.d.ts.map +1 -0
- package/dist/modes/tui/view-state.js +353 -0
- package/dist/prompts/agents/subagent.md +26 -0
- package/dist/prompts/system/base.md +20 -0
- package/dist/prompts/system/memory.md +23 -0
- package/dist/prompts/system/safety.md +12 -0
- package/dist/protocol/index.d.ts +5 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/index.js +2 -0
- package/dist/protocol/mapping.d.ts +25 -0
- package/dist/protocol/mapping.d.ts.map +1 -0
- package/dist/protocol/mapping.js +115 -0
- package/dist/protocol/transport.d.ts +1396 -0
- package/dist/protocol/transport.d.ts.map +1 -0
- package/dist/protocol/transport.js +170 -0
- package/package.json +49 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** Strict LF-framed JSONL reader (Pi-style).
|
|
2
|
+
*
|
|
3
|
+
* - Only `\n` (0x0A) splits frames - a generic line reader that also splits
|
|
4
|
+
* on Unicode separators would corrupt payloads.
|
|
5
|
+
* - Byte-buffer based: half packets accumulate until the next LF.
|
|
6
|
+
* - A single frame is bounded (`maxLineBytes`); oversized frames are skipped
|
|
7
|
+
* and reported via `onOversize`. The buffer is trimmed so a hostile peer
|
|
8
|
+
* cannot grow memory without bound - trimmed bytes belong to an oversized
|
|
9
|
+
* frame, which is discarded up to its terminating LF. */
|
|
10
|
+
export function createJsonlReader(stream, opts = {}) {
|
|
11
|
+
const maxLineBytes = opts.maxLineBytes ?? 16 * 1024 * 1024;
|
|
12
|
+
const reader = stream.getReader();
|
|
13
|
+
const lines = (async function* () {
|
|
14
|
+
const decoder = new TextDecoder("utf-8");
|
|
15
|
+
let buffer = new Uint8Array(0);
|
|
16
|
+
// True while the buffered bytes belong to a frame already known to
|
|
17
|
+
// exceed the bound: everything up to the next LF is discarded.
|
|
18
|
+
let inOversize = false;
|
|
19
|
+
try {
|
|
20
|
+
for (;;) {
|
|
21
|
+
const { done, value } = await reader.read();
|
|
22
|
+
if (done)
|
|
23
|
+
break;
|
|
24
|
+
if (value.length === 0)
|
|
25
|
+
continue;
|
|
26
|
+
// Append the chunk.
|
|
27
|
+
const next = new Uint8Array(buffer.length + value.length);
|
|
28
|
+
next.set(buffer, 0);
|
|
29
|
+
next.set(value, buffer.length);
|
|
30
|
+
buffer = next;
|
|
31
|
+
// Split on LF only.
|
|
32
|
+
let start = 0;
|
|
33
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
34
|
+
if (buffer[i] !== 0x0a)
|
|
35
|
+
continue;
|
|
36
|
+
const lineLen = i - start;
|
|
37
|
+
if (inOversize) {
|
|
38
|
+
// The LF terminates the truncated oversized frame: discard it.
|
|
39
|
+
inOversize = false;
|
|
40
|
+
}
|
|
41
|
+
else if (lineLen <= maxLineBytes) {
|
|
42
|
+
yield decoder.decode(buffer.subarray(start, i));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// The frame ended at this LF (it is complete but oversized).
|
|
46
|
+
opts.onOversize?.(lineLen);
|
|
47
|
+
}
|
|
48
|
+
start = i + 1;
|
|
49
|
+
}
|
|
50
|
+
buffer = buffer.slice(start);
|
|
51
|
+
// Memory bound: a partial frame longer than the bound can never
|
|
52
|
+
// become a valid line - drop it, discard up to the next LF.
|
|
53
|
+
if (buffer.length > maxLineBytes) {
|
|
54
|
+
inOversize = true;
|
|
55
|
+
buffer = new Uint8Array(0);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Trailing frame without a final LF.
|
|
59
|
+
if (buffer.length > 0) {
|
|
60
|
+
if (!inOversize && buffer.length <= maxLineBytes) {
|
|
61
|
+
yield decoder.decode(buffer);
|
|
62
|
+
}
|
|
63
|
+
else if (!inOversize) {
|
|
64
|
+
opts.onOversize?.(buffer.length);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
try {
|
|
70
|
+
reader.releaseLock();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
/* already released */
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
})();
|
|
77
|
+
return {
|
|
78
|
+
lines,
|
|
79
|
+
async cancel() {
|
|
80
|
+
try {
|
|
81
|
+
await reader.cancel();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
/* stream already closed */
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ModelRuntime } from "@chengchenccc/ai";
|
|
2
|
+
/** Minimal RPC mode: stdin JSONL commands, stdout JSONL outputs only, stderr
|
|
3
|
+
* for logs. One process = at most one execute = one Run = one outcome, then
|
|
4
|
+
* the process exits. No Session lifecycle, no HTTP, no registry. */
|
|
5
|
+
export interface RpcModeOptions {
|
|
6
|
+
modelRuntime: ModelRuntime;
|
|
7
|
+
stdin?: ReadableStream<Uint8Array>;
|
|
8
|
+
/** stdout writer; the RPC mode ONLY writes JSONL lines here. */
|
|
9
|
+
writeLine?: (line: string) => void;
|
|
10
|
+
log?: (line: string) => void;
|
|
11
|
+
}
|
|
12
|
+
export interface RpcModeController {
|
|
13
|
+
readonly promise: Promise<number>;
|
|
14
|
+
/** Abort the live Run (SIGINT/SIGTERM path): outcome settles aborted. */
|
|
15
|
+
stop(): void;
|
|
16
|
+
}
|
|
17
|
+
export declare function runRpcMode(opts: RpcModeOptions): RpcModeController;
|
|
18
|
+
//# sourceMappingURL=rpc-mode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-mode.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/rpc-mode.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAmCrD;;qEAEqE;AAErE,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,YAAY,CAAC;IAC3B,KAAK,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACnC,gEAAgE;IAChE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,yEAAyE;IACzE,IAAI,IAAI,IAAI,CAAC;CACd;AAMD,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,iBAAiB,CA0VlE"}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { existsSync, statSync } from "node:fs";
|
|
2
|
+
import { debugLog } from "@chengchenccc/agent-contract";
|
|
3
|
+
import { MessageSchema } from "@chengchenccc/message";
|
|
4
|
+
import { assemblePluginRuntime } from "../../core/plugins/plugin-resolve.js";
|
|
5
|
+
import { approvalTimeoutMs, withApprovalDeadline, } from "../../core/runtime/approval.js";
|
|
6
|
+
import { createOmaRuntime } from "../../core/runtime/create-runtime.js";
|
|
7
|
+
import { buildSystemPrompt, readMemorySummary } from "../../core/runtime/prompts.js";
|
|
8
|
+
import { appendSessionCompaction, appendSessionMessages, loadSessionMessages, newSessionId, } from "../../core/session/session-file.js";
|
|
9
|
+
import { readWorkspaceSystemPrompt, scanWorkspaceSkillRoots, } from "../../core/settings/workspace-context.js";
|
|
10
|
+
import { codingAgentCommandSchema, eventOutputSchema, outcomeOutputSchema, responseOutputSchema, } from "../../protocol/index.js";
|
|
11
|
+
import { createJsonlReader } from "./jsonl.js";
|
|
12
|
+
function redactError(err) {
|
|
13
|
+
return err instanceof Error ? err.message : String(err);
|
|
14
|
+
}
|
|
15
|
+
export function runRpcMode(opts) {
|
|
16
|
+
const stdin = opts.stdin ?? Bun.stdin.stream();
|
|
17
|
+
const write = opts.writeLine ?? ((line) => process.stdout.write(line));
|
|
18
|
+
const log = opts.log ?? ((line) => process.stderr.write(`${line}\n`));
|
|
19
|
+
// Serialized output: whole lines, in order, never interleaved.
|
|
20
|
+
let writeChain = Promise.resolve();
|
|
21
|
+
const emit = (output) => {
|
|
22
|
+
const line = JSON.stringify(output);
|
|
23
|
+
writeChain = writeChain.then(() => {
|
|
24
|
+
write(`${line}\n`);
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
const emitResponse = (id, command, success, error) => {
|
|
28
|
+
emit(responseOutputSchema.parse({
|
|
29
|
+
id,
|
|
30
|
+
type: "response",
|
|
31
|
+
command,
|
|
32
|
+
success,
|
|
33
|
+
...(success ? {} : { error: error ?? "command failed" }),
|
|
34
|
+
}));
|
|
35
|
+
};
|
|
36
|
+
let runtime = null;
|
|
37
|
+
let currentRunId = null;
|
|
38
|
+
let executed = false;
|
|
39
|
+
let finished = false;
|
|
40
|
+
const reader = createJsonlReader(stdin);
|
|
41
|
+
/** runId → (callId → resolver). Late/unknown resolutions fail soft. */
|
|
42
|
+
const pendingApprovalsByRun = new Map();
|
|
43
|
+
const promise = (async () => {
|
|
44
|
+
try {
|
|
45
|
+
for await (const line of reader.lines) {
|
|
46
|
+
if (finished) {
|
|
47
|
+
// The Run settled: the process exits. One more line is read so a
|
|
48
|
+
// protocol-violating second execute gets an explicit rejection;
|
|
49
|
+
// anything else (or EOF) just ends the process.
|
|
50
|
+
if (line.trim()) {
|
|
51
|
+
try {
|
|
52
|
+
const command = codingAgentCommandSchema.parse(JSON.parse(line));
|
|
53
|
+
if (command.type === "execute") {
|
|
54
|
+
emitResponse(command.id, "execute", false, "a process accepts at most one execute command");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
/* ignored: the process is exiting */
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
if (!line.trim())
|
|
64
|
+
continue;
|
|
65
|
+
let command;
|
|
66
|
+
try {
|
|
67
|
+
command = codingAgentCommandSchema.parse(JSON.parse(line));
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Malformed JSON: a failure response keeps the protocol clean; the
|
|
71
|
+
// peer settles the Run failed on an uncorrelated response.
|
|
72
|
+
log(`malformed command (${line.length} bytes)`);
|
|
73
|
+
emit(responseOutputSchema.parse({
|
|
74
|
+
id: "",
|
|
75
|
+
type: "response",
|
|
76
|
+
command: "execute",
|
|
77
|
+
success: false,
|
|
78
|
+
error: "malformed JSON command",
|
|
79
|
+
}));
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
switch (command.type) {
|
|
83
|
+
case "execute": {
|
|
84
|
+
if (executed) {
|
|
85
|
+
// Protocol invariant: one process → at most one execute.
|
|
86
|
+
emitResponse(command.id, "execute", false, "a process accepts at most one execute command");
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
executed = true; // consumed synchronously: no double-execute race
|
|
90
|
+
// Acceptance is AWAITED so the success response is always the
|
|
91
|
+
// first output; the loop itself runs concurrently below so
|
|
92
|
+
// steer/abort written after acceptance stay routable.
|
|
93
|
+
await acceptExecute(command);
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
case "steer":
|
|
97
|
+
handleSteer(command);
|
|
98
|
+
break;
|
|
99
|
+
case "abort":
|
|
100
|
+
handleAbort(command);
|
|
101
|
+
break;
|
|
102
|
+
case "resolve_approval": {
|
|
103
|
+
const resolve = pendingApprovalsByRun.get(command.runId)?.get(command.callId);
|
|
104
|
+
if (resolve) {
|
|
105
|
+
resolve({ decision: command.decision });
|
|
106
|
+
emitResponse(command.id, "resolve_approval", true);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
emitResponse(command.id, "resolve_approval", false, `no pending approval ${command.callId}`);
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (!finished) {
|
|
116
|
+
log(`stdin closed before an outcome was produced (executed=${executed})`);
|
|
117
|
+
return 1;
|
|
118
|
+
}
|
|
119
|
+
return await writeChain.then(() => 0);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
log(`rpc mode failed: ${redactError(err)}`);
|
|
123
|
+
return 1;
|
|
124
|
+
}
|
|
125
|
+
})();
|
|
126
|
+
/** Validate + assemble the Runtime, START the loop, and emit the execute
|
|
127
|
+
* response ONLY once the loop is live (agent_start). Awaited by the
|
|
128
|
+
* reader so the acceptance response is always the first output AND
|
|
129
|
+
* implies steer/abort are routable; the outcome runs CONCURRENTLY via
|
|
130
|
+
* driveOutcome, keeping steer/abort routable for the whole run. */
|
|
131
|
+
async function acceptExecute(command) {
|
|
132
|
+
const input = command.input;
|
|
133
|
+
debugLog("oma", `execute_received runId=${input.run.runId}`);
|
|
134
|
+
const err = await validateExecute(input, opts.modelRuntime);
|
|
135
|
+
if (err) {
|
|
136
|
+
emitResponse(command.id, "execute", false, err);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const runId = input.run.runId;
|
|
140
|
+
// Session (ADR 0003 decision 6): the child owns its session file; the
|
|
141
|
+
// product forwards the branch's opaque reference. Resume loads the
|
|
142
|
+
// transcript as the loop's seed history (validated at the file
|
|
143
|
+
// boundary); a ref whose file is missing/empty degrades to a fresh
|
|
144
|
+
// session (the first-turn bridge already lives in the input message).
|
|
145
|
+
const resumeId = input.run.cliSessionRef;
|
|
146
|
+
const sessionId = resumeId ?? newSessionId();
|
|
147
|
+
const loaded = resumeId ? loadSessionMessages(resumeId) : [];
|
|
148
|
+
// A corrupt line must degrade that entry (log + skip), never brick the
|
|
149
|
+
// whole resume: MessageSchema.parse throws on the first bad shape.
|
|
150
|
+
const parsedTranscript = [];
|
|
151
|
+
for (const [i, message] of loaded.entries()) {
|
|
152
|
+
try {
|
|
153
|
+
parsedTranscript.push({
|
|
154
|
+
productEntryId: `session:${i}`,
|
|
155
|
+
message: MessageSchema.parse(message),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
console.warn(`[rpc] skipping malformed session line ${i} for ${resumeId}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const sessionTranscript = parsedTranscript.length > 0 ? parsedTranscript : undefined;
|
|
163
|
+
let effectiveInput;
|
|
164
|
+
let segment;
|
|
165
|
+
try {
|
|
166
|
+
// cwd-based meta (ADR 0003 decision 6): skills and the system prompt
|
|
167
|
+
// live in workspace files (.oma/skills + AGENTS.md/SOUL.md/USER.md).
|
|
168
|
+
// Explicit run-input values (Loop scopes) win over the cwd fallback.
|
|
169
|
+
const cwdSkills = scanWorkspaceSkillRoots(input.workspace.root);
|
|
170
|
+
const cwdPrompt = readWorkspaceSystemPrompt(input.workspace.root);
|
|
171
|
+
effectiveInput = input.run.systemPrompt
|
|
172
|
+
? input
|
|
173
|
+
: {
|
|
174
|
+
...input,
|
|
175
|
+
run: {
|
|
176
|
+
...input.run,
|
|
177
|
+
systemPrompt: buildSystemPrompt({
|
|
178
|
+
workspacePrompt: cwdPrompt,
|
|
179
|
+
memorySummary: readMemorySummary(input.workspace.root),
|
|
180
|
+
cwd: input.workspace.root,
|
|
181
|
+
}),
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
// Plugin components (spec): policy resolved in the mode layer — RPC
|
|
185
|
+
// NEVER loads project-scope code; user-scope needs enablement only.
|
|
186
|
+
const pluginRt = await assemblePluginRuntime(input.workspace.root, "rpc");
|
|
187
|
+
for (const w of pluginRt.warnings)
|
|
188
|
+
debugLog("oma", `plugin: ${w}`);
|
|
189
|
+
// HITL approval pipe (spec): emit approval_request on stdout, park the
|
|
190
|
+
// resolver, resolve on the resolve_approval command; deadline = deny.
|
|
191
|
+
const pendingApprovals = new Map();
|
|
192
|
+
pendingApprovalsByRun.set(runId, pendingApprovals);
|
|
193
|
+
let approvalSeq = 10_000;
|
|
194
|
+
const rpcApproval = (req) => new Promise((resolve) => {
|
|
195
|
+
pendingApprovals.set(req.callId, resolve);
|
|
196
|
+
emit(eventOutputSchema.parse({
|
|
197
|
+
type: "event",
|
|
198
|
+
runId,
|
|
199
|
+
// Own id space (10k+): the runtime's envelope seq is internal;
|
|
200
|
+
// consumers key on type + data.callId, not global id order.
|
|
201
|
+
event: {
|
|
202
|
+
id: approvalSeq++,
|
|
203
|
+
type: "approval_request",
|
|
204
|
+
data: {
|
|
205
|
+
callId: req.callId,
|
|
206
|
+
toolName: req.toolName,
|
|
207
|
+
reason: req.reason ?? `${req.toolName} requested approval (${req.source})`,
|
|
208
|
+
input: req.input,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
}));
|
|
212
|
+
});
|
|
213
|
+
runtime = await createOmaRuntime({
|
|
214
|
+
runId,
|
|
215
|
+
modelId: input.run.model.modelId,
|
|
216
|
+
workspaceRoot: input.workspace.root,
|
|
217
|
+
workspaceAccess: input.workspace.access,
|
|
218
|
+
modelRuntime: opts.modelRuntime,
|
|
219
|
+
skillRoots: input.run.skillRoots?.length ? input.run.skillRoots : cwdSkills,
|
|
220
|
+
...(pluginRt.plugins.length || pluginRt.mcpServers.length
|
|
221
|
+
? { pluginComponents: { plugins: pluginRt.plugins, mcpServers: pluginRt.mcpServers } }
|
|
222
|
+
: {}),
|
|
223
|
+
...(input.run.permissionMode ? { permissionMode: input.run.permissionMode } : {}),
|
|
224
|
+
approvalHandler: (req) => withApprovalDeadline(rpcApproval(req), approvalTimeoutMs()),
|
|
225
|
+
sessionTranscript,
|
|
226
|
+
onEvent: (event) => {
|
|
227
|
+
if (!finished)
|
|
228
|
+
emit(eventOutputSchema.parse({ type: "event", runId, event }));
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
// run() resolves when the loop is live: acceptance ⟹ routable.
|
|
232
|
+
segment = await runtime.run(effectiveInput);
|
|
233
|
+
debugLog("oma", `runtime_assembled runId=${runId} skills=${(input.run.skillRoots?.length ? input.run.skillRoots : cwdSkills).length} access=${input.workspace.access}`);
|
|
234
|
+
}
|
|
235
|
+
catch (caught) {
|
|
236
|
+
emitResponse(command.id, "execute", false, `runtime assembly failed: ${redactError(caught)}`);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
// Acceptance: the runtime is assembled, event forwarding is registered,
|
|
240
|
+
// the loop is live, and steer/abort route to it.
|
|
241
|
+
currentRunId = runId;
|
|
242
|
+
debugLog("oma", `loop_live runId=${runId}`);
|
|
243
|
+
emitResponse(command.id, "execute", true, undefined);
|
|
244
|
+
void driveOutcome(runtime, segment, runId, sessionId, effectiveInput.input.message);
|
|
245
|
+
}
|
|
246
|
+
/** Await the outcome, emit the outcome envelope, flush, close the runtime,
|
|
247
|
+
* then END the reader so the process exits on its own (one Run → one
|
|
248
|
+
* outcome → exit) - no dependency on the parent closing stdin. */
|
|
249
|
+
async function driveOutcome(runtime, segment, runId, sessionId, inputMessage) {
|
|
250
|
+
let outcome;
|
|
251
|
+
try {
|
|
252
|
+
outcome = await segment.outcome;
|
|
253
|
+
}
|
|
254
|
+
catch (caught) {
|
|
255
|
+
outcome = { status: "failed", error: redactError(caught) };
|
|
256
|
+
}
|
|
257
|
+
// Persist the turn into the child's session file (user + assistant/tool
|
|
258
|
+
// messages) so the next run resumes the transcript (ADR 0003).
|
|
259
|
+
if (outcome.status === "completed" && outcome.messages?.length) {
|
|
260
|
+
appendSessionMessages(sessionId, process.cwd(), [inputMessage, ...outcome.messages]);
|
|
261
|
+
for (const summary of await runtime.compactions()) {
|
|
262
|
+
appendSessionCompaction(sessionId, summary);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const outcomeWithRef = {
|
|
266
|
+
...outcome,
|
|
267
|
+
cliSessionRef: sessionId,
|
|
268
|
+
};
|
|
269
|
+
finished = true;
|
|
270
|
+
await runtime.close().catch(() => { });
|
|
271
|
+
debugLog("oma", `runtime_closed runId=${runId}`);
|
|
272
|
+
emit(outcomeOutputSchema.parse({ type: "outcome", runId, outcome: outcomeWithRef }));
|
|
273
|
+
debugLog("oma", `outcome runId=${runId} status=${outcome.status}`);
|
|
274
|
+
await writeChain;
|
|
275
|
+
// Unblock the reader: the pending stdin read resolves done and the main
|
|
276
|
+
// promise returns; main() exits with the code. Never a hard process.exit
|
|
277
|
+
// before the outcome is written and the runtime closed.
|
|
278
|
+
await reader.cancel();
|
|
279
|
+
debugLog("oma", `rpc_exit runId=${runId}`);
|
|
280
|
+
}
|
|
281
|
+
function handleSteer(command) {
|
|
282
|
+
debugLog("oma", `steer_received runId=${command.runId}`);
|
|
283
|
+
if (!executed || command.runId !== currentRunId || !runtime) {
|
|
284
|
+
emitResponse(command.id, "steer", false, `no live run for runId: ${command.runId} (current: ${currentRunId ?? "none"})`);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
void runtime.steer(command.input).then(() => emitResponse(command.id, "steer", true), (err) => emitResponse(command.id, "steer", false, redactError(err)));
|
|
289
|
+
}
|
|
290
|
+
catch (caught) {
|
|
291
|
+
emitResponse(command.id, "steer", false, redactError(caught));
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function handleAbort(command) {
|
|
295
|
+
debugLog("oma", `abort_received runId=${command.runId}`);
|
|
296
|
+
if (!executed || command.runId !== currentRunId || !runtime) {
|
|
297
|
+
emitResponse(command.id, "abort", false, `no live run for runId: ${command.runId} (current: ${currentRunId ?? "none"})`);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
void runtime.stop().then(() => emitResponse(command.id, "abort", true), (err) => emitResponse(command.id, "abort", false, redactError(err)));
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
promise,
|
|
304
|
+
stop() {
|
|
305
|
+
if (runtime)
|
|
306
|
+
void runtime.stop().catch(() => { });
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
/** Execute acceptance preflight: payload schema valid (already parsed),
|
|
311
|
+
* model valid/available in the runtime catalog, workspace valid. */
|
|
312
|
+
async function validateExecute(input, modelRuntime) {
|
|
313
|
+
if (input.run.model.backendKind !== "oma") {
|
|
314
|
+
return `unsupported backend kind: ${input.run.model.backendKind}`;
|
|
315
|
+
}
|
|
316
|
+
if (!existsSync(input.workspace.root) || !statSync(input.workspace.root).isDirectory()) {
|
|
317
|
+
return `workspace root is not a directory: ${input.workspace.root}`;
|
|
318
|
+
}
|
|
319
|
+
const catalog = await modelRuntime.getCatalog();
|
|
320
|
+
const model = catalog.models.find((m) => `${m.providerId}/${m.modelId}` === input.run.model.modelId);
|
|
321
|
+
if (!model)
|
|
322
|
+
return `model not found in catalog: ${input.run.model.modelId}`;
|
|
323
|
+
if (model.available === false)
|
|
324
|
+
return `model unavailable: ${input.run.model.modelId}`;
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Container } from "@chengchenccc/tui";
|
|
2
|
+
import type { ProjectSettings } from "../../core/settings/project-settings.js";
|
|
3
|
+
/** Modal settings editor (omp SettingsSelector-container-inspired but lean):
|
|
4
|
+
* a SelectList of rows; Enter toggles booleans or opens a text Input for
|
|
5
|
+
* numeric/string values; Esc closes. Changes are held in a local copy. */
|
|
6
|
+
export declare class SettingsOverlay extends Container {
|
|
7
|
+
private readonly onDone;
|
|
8
|
+
private readonly settings;
|
|
9
|
+
private list;
|
|
10
|
+
private readonly listSlot;
|
|
11
|
+
private editing;
|
|
12
|
+
constructor(settings: ProjectSettings, onDone: () => void);
|
|
13
|
+
getSettings(): ProjectSettings;
|
|
14
|
+
private displayValue;
|
|
15
|
+
private itemFor;
|
|
16
|
+
private rebuild;
|
|
17
|
+
private handleSelect;
|
|
18
|
+
private beginEdit;
|
|
19
|
+
private commitEdit;
|
|
20
|
+
handleInput(data: string): void;
|
|
21
|
+
render(width: number): string[];
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=settings-overlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-overlay.d.ts","sourceRoot":"","sources":["../../../src/modes/tui/settings-overlay.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,SAAS,EAKV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAmD/E;;2EAE2E;AAC3E,qBAAa,eAAgB,SAAQ,SAAS;IAQ1C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8B;IACvD,OAAO,CAAC,OAAO,CAAkD;gBAG/D,QAAQ,EAAE,eAAe,EACR,MAAM,EAAE,MAAM,IAAI;IAOrC,WAAW,IAAI,eAAe;IAI9B,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,OAAO;IAiBf,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,UAAU;IAgBlB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQtB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;CAUzC"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { applyBackgroundToLine, Container, Input, SelectList, truncateToWidth, } from "@chengchenccc/tui";
|
|
2
|
+
const OVERLAY_BG = (s) => `\u001b[48;5;235m${s}\u001b[0m`;
|
|
3
|
+
function overlayLines(lines, width) {
|
|
4
|
+
const innerWidth = Math.max(1, width - 2);
|
|
5
|
+
return lines.map((line) => {
|
|
6
|
+
const content = truncateToWidth(line, innerWidth, "", true);
|
|
7
|
+
return `\u001b[36m\u2502\u001b[0m${applyBackgroundToLine(content, innerWidth, OVERLAY_BG)}\u001b[36m\u2502\u001b[0m`;
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
const THEME = {
|
|
11
|
+
selectedPrefix: (s) => `\u001b[36m${s}\u001b[0m`,
|
|
12
|
+
selectedText: (s) => `\u001b[1m${s}\u001b[0m`,
|
|
13
|
+
description: (s) => `\u001b[2m${s}\u001b[0m`,
|
|
14
|
+
scrollInfo: (s) => `\u001b[2m${s}\u001b[0m`,
|
|
15
|
+
noMatch: (s) => `\u001b[2m${s}\u001b[0m`,
|
|
16
|
+
};
|
|
17
|
+
const ROWS = [
|
|
18
|
+
{ key: "maxSteps", label: "maxSteps", kind: "number" },
|
|
19
|
+
{ key: "modelTimeoutMs", label: "modelTimeoutMs", kind: "number" },
|
|
20
|
+
{ key: "mcpTimeoutMs", label: "mcpTimeoutMs", kind: "number" },
|
|
21
|
+
{ key: "bashTimeoutMs", label: "bashTimeoutMs", kind: "number" },
|
|
22
|
+
{ key: "maxToolTimeoutMs", label: "maxToolTimeoutMs", kind: "number" },
|
|
23
|
+
{ key: "memoryExtract", label: "memoryExtract", kind: "boolean" },
|
|
24
|
+
{ key: "memoryModel", label: "memoryModel", kind: "string" },
|
|
25
|
+
{ key: "titleEnabled", label: "titleEnabled", kind: "boolean" },
|
|
26
|
+
{ key: "disableWeb", label: "disableWeb", kind: "boolean" },
|
|
27
|
+
{ key: "enableClaude", label: "enableClaude", kind: "boolean" },
|
|
28
|
+
{ key: "enableCodex", label: "enableCodex", kind: "boolean" },
|
|
29
|
+
{ key: "enableAgents", label: "enableAgents", kind: "boolean" },
|
|
30
|
+
];
|
|
31
|
+
function rowValue(settings, key) {
|
|
32
|
+
const v = settings[key];
|
|
33
|
+
if (typeof v === "boolean")
|
|
34
|
+
return v ? "on" : "off";
|
|
35
|
+
if (v === undefined || v === null || v === "")
|
|
36
|
+
return "(unset)";
|
|
37
|
+
if (Array.isArray(v))
|
|
38
|
+
return v.length > 0 ? `${v.length} dirs` : "(none)";
|
|
39
|
+
return String(v);
|
|
40
|
+
}
|
|
41
|
+
/** Modal settings editor (omp SettingsSelector-container-inspired but lean):
|
|
42
|
+
* a SelectList of rows; Enter toggles booleans or opens a text Input for
|
|
43
|
+
* numeric/string values; Esc closes. Changes are held in a local copy. */
|
|
44
|
+
export class SettingsOverlay extends Container {
|
|
45
|
+
onDone;
|
|
46
|
+
settings;
|
|
47
|
+
list;
|
|
48
|
+
listSlot = new Container();
|
|
49
|
+
editing = null;
|
|
50
|
+
constructor(settings, onDone) {
|
|
51
|
+
super();
|
|
52
|
+
this.onDone = onDone;
|
|
53
|
+
this.settings = { ...settings };
|
|
54
|
+
this.rebuild();
|
|
55
|
+
}
|
|
56
|
+
getSettings() {
|
|
57
|
+
return this.settings;
|
|
58
|
+
}
|
|
59
|
+
displayValue(key) {
|
|
60
|
+
return rowValue(this.settings, key);
|
|
61
|
+
}
|
|
62
|
+
itemFor(row) {
|
|
63
|
+
return {
|
|
64
|
+
value: row.key,
|
|
65
|
+
label: row.label,
|
|
66
|
+
description: `${row.kind} · ${this.displayValue(row.key)}`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
rebuild() {
|
|
70
|
+
this.list = new SelectList(ROWS.map((row) => this.itemFor(row)), 12, THEME, { minPrimaryColumnWidth: 16, maxPrimaryColumnWidth: 24 });
|
|
71
|
+
this.list.onSelect = (item) => {
|
|
72
|
+
const row = ROWS.find((r) => r.key === item.value);
|
|
73
|
+
if (row)
|
|
74
|
+
this.handleSelect(row);
|
|
75
|
+
};
|
|
76
|
+
this.list.onCancel = () => this.onDone();
|
|
77
|
+
this.listSlot.clear();
|
|
78
|
+
this.listSlot.addChild(this.list);
|
|
79
|
+
this.invalidate();
|
|
80
|
+
}
|
|
81
|
+
handleSelect(row) {
|
|
82
|
+
if (row.kind === "boolean") {
|
|
83
|
+
const current = this.settings[row.key];
|
|
84
|
+
const next = typeof current === "boolean" ? !current : true;
|
|
85
|
+
Reflect.set(this.settings, row.key, next);
|
|
86
|
+
this.rebuild();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
this.beginEdit(row);
|
|
90
|
+
}
|
|
91
|
+
beginEdit(row) {
|
|
92
|
+
const input = new Input();
|
|
93
|
+
input.setValue(this.displayValue(row.key) === "(unset)" ? "" : String(this.settings[row.key]));
|
|
94
|
+
input.onSubmit = () => {
|
|
95
|
+
this.commitEdit(row, input.getValue());
|
|
96
|
+
};
|
|
97
|
+
input.onEscape = () => {
|
|
98
|
+
this.editing = null;
|
|
99
|
+
this.invalidate();
|
|
100
|
+
};
|
|
101
|
+
this.editing = { row, input };
|
|
102
|
+
this.invalidate();
|
|
103
|
+
}
|
|
104
|
+
commitEdit(row, raw) {
|
|
105
|
+
let value = raw.trim();
|
|
106
|
+
if (row.kind === "number") {
|
|
107
|
+
const parsed = Number(raw.trim());
|
|
108
|
+
if (!Number.isFinite(parsed)) {
|
|
109
|
+
this.editing = null;
|
|
110
|
+
this.invalidate();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
value = parsed;
|
|
114
|
+
}
|
|
115
|
+
Reflect.set(this.settings, row.key, value);
|
|
116
|
+
this.editing = null;
|
|
117
|
+
this.rebuild();
|
|
118
|
+
}
|
|
119
|
+
handleInput(data) {
|
|
120
|
+
if (this.editing) {
|
|
121
|
+
this.editing.input.handleInput(data);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
this.list.handleInput(data);
|
|
125
|
+
}
|
|
126
|
+
render(width) {
|
|
127
|
+
const lines = [];
|
|
128
|
+
lines.push(" settings — enter to edit, esc to close");
|
|
129
|
+
if (this.editing) {
|
|
130
|
+
lines.push(` edit ${this.editing.row.label} (${this.editing.row.kind}):`);
|
|
131
|
+
lines.push(...this.editing.input.render(Math.max(1, width - 4)));
|
|
132
|
+
}
|
|
133
|
+
lines.push(...this.list.render(Math.max(1, width - 2)));
|
|
134
|
+
return overlayLines(lines, width);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tui-commands-session.d.ts","sourceRoot":"","sources":["../../../src/modes/tui/tui-commands-session.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGvE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,iBAAiB,GAAG,UAAU,EAAE,CAiMzE"}
|