@linzumi/cli 0.0.20-beta → 0.0.23-beta

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.
@@ -1,142 +0,0 @@
1
- /*
2
- - Date: 2026-04-24
3
- Spec: plans/2026-04-24-local-codex-channel-thread-binding-spec.md
4
- Relationship: Provides the headless local-runner stdout status feed required
5
- for operators to see accepted, ignored, forwarded, and Codex-output events
6
- without printing message bodies or raw Codex payload contents.
7
-
8
- - Date: 2026-04-25
9
- Spec: plans/2026-04-24-local-codex-runner-deep-quality-spec.md
10
- Relationship: Surfaces live reasoning, command-output, and search-progress
11
- projection status in headless runner stdout while keeping transcript content
12
- out of the console stream.
13
- */
14
-
15
- type ConsolePayload = Record<string, unknown>;
16
-
17
- export function reportRunnerConsoleEvent(event: string, payload: ConsolePayload): void {
18
- const line = formatRunnerConsoleEvent(event, payload);
19
-
20
- if (line !== undefined) {
21
- process.stdout.write(`${line}\n`);
22
- }
23
- }
24
-
25
- export function formatRunnerConsoleEvent(
26
- event: string,
27
- payload: ConsolePayload,
28
- ): string | undefined {
29
- switch (event) {
30
- case "runner.instance_started":
31
- return `Runner connected: instance=${text(payload.instanceId)} codex=${text(payload.codexUrl)}`;
32
-
33
- case "kandan.message_ignored":
34
- return `Incoming message from ${sender(payload)}: ignored for reason ${text(payload.reason)}`;
35
-
36
- case "kandan.message_queued":
37
- return `Incoming message from ${sender(payload)}: queued seq=${text(payload.seq)} depth=${text(payload.queue_depth)}`;
38
-
39
- case "kandan.chat_event_failed":
40
- return `Incoming message handling failed: seq=${text(payload.seq)} reason=${text(payload.message)}`;
41
-
42
- case "kandan.reconnected":
43
- return `Kandan reconnected: codex_session=${text(payload.codex_thread_id)} cursor=${text(payload.min_seq)}`;
44
-
45
- case "codex.turn_starting":
46
- return `Incoming message from ${sender(payload)}: forwarding to Codex session ${text(payload.codex_thread_id)} seq=${text(payload.queued_seq)}`;
47
-
48
- case "codex.turn_started":
49
- return `Codex turn started: id=${text(payload.turn_id)}`;
50
-
51
- case "codex.notification":
52
- return `Codex event [id=${codexEventId(payload)}]: ${text(payload.method)}`;
53
-
54
- case "codex.turn_completed":
55
- return `Codex turn completed: id=${text(payload.turn_id)} outputs=${text(payload.output_count)}`;
56
-
57
- case "kandan.codex_output_forwarded":
58
- return `Codex event [id=${text(payload.item_key)}]: ${codexOutputLabel(payload)}`;
59
-
60
- case "kandan.codex_reasoning_delta_forwarded":
61
- return `Codex event [id=${text(payload.item_key)}]: reasoning_delta chars=${text(payload.content_length)}`;
62
-
63
- case "kandan.codex_command_output_forwarded":
64
- return `Codex event [id=${text(payload.item_key)}]: command_output ${text(payload.stream)} chars=${text(payload.output_length)}`;
65
-
66
- case "kandan.codex_web_search_progress_forwarded":
67
- return `Codex event [id=${text(payload.item_key)}]: search_progress queries=${text(payload.query_count)}`;
68
-
69
- case "codex.queued_messages_interrupted":
70
- return `Queued messages interrupted: selected=${text(payload.selected_count)} remaining=${text(payload.remaining_count)}`;
71
-
72
- case "codex.turn_start_failed":
73
- return `Codex turn start failed: seq=${text(payload.queued_seq)} reason=${text(payload.message)}`;
74
-
75
- case "codex.turn_forward_failed":
76
- return `Codex turn forward failed: id=${text(payload.turn_id)} reason=${text(payload.message)}`;
77
-
78
- default:
79
- return undefined;
80
- }
81
- }
82
-
83
- function sender(payload: ConsolePayload): string {
84
- const slug = stringValue(payload.actor_slug);
85
- const userId = numberValue(payload.actor_user_id);
86
-
87
- if (slug !== undefined && userId !== undefined) {
88
- return `${slug}#${userId}`;
89
- }
90
-
91
- return slug ?? (userId === undefined ? "unknown" : `user#${userId}`);
92
- }
93
-
94
- function codexOutputLabel(payload: ConsolePayload): string {
95
- const kind = stringValue(payload.structured_kind) ?? "output";
96
-
97
- switch (kind) {
98
- case "codex_assistant_message":
99
- return "assistant_message";
100
- case "codex_reasoning":
101
- return "reasoning";
102
- case "codex_command_execution":
103
- return `command ${text(payload.command)}`;
104
- case "codex_terminal_input":
105
- return "terminal_input";
106
- case "codex_file_change":
107
- return `file_change ${fileChangePaths(payload)}`;
108
- default:
109
- return kind;
110
- }
111
- }
112
-
113
- function fileChangePaths(payload: ConsolePayload): string {
114
- const paths = Array.isArray(payload.file_paths)
115
- ? payload.file_paths.filter((value): value is string => typeof value === "string")
116
- : [];
117
-
118
- return paths.length === 0 ? "(unknown file)" : paths.slice(0, 3).join(", ");
119
- }
120
-
121
- function codexEventId(payload: ConsolePayload): string {
122
- const metadata = payload.metadata;
123
-
124
- if (typeof metadata === "object" && metadata !== null && !Array.isArray(metadata)) {
125
- const record = metadata as Record<string, unknown>;
126
- return stringValue(record.turnId) ?? stringValue(record.itemId) ?? stringValue(record.threadId) ?? "?";
127
- }
128
-
129
- return "?";
130
- }
131
-
132
- function text(value: unknown): string {
133
- return stringValue(value) ?? numberValue(value)?.toString() ?? "?";
134
- }
135
-
136
- function stringValue(value: unknown): string | undefined {
137
- return typeof value === "string" && value.trim() !== "" ? value : undefined;
138
- }
139
-
140
- function numberValue(value: unknown): number | undefined {
141
- return typeof value === "number" && Number.isFinite(value) ? value : undefined;
142
- }
@@ -1,50 +0,0 @@
1
- /*
2
- - Date: 2026-04-24
3
- Spec: plans/2026-04-24-local-codex-channel-thread-binding-spec.md
4
- Relationship: Writes the runner's local event log without blocking the
5
- websocket/control loop on synchronous filesystem appends.
6
- */
7
- import { openSync } from "node:fs";
8
- import { createWriteStream, type WriteStream } from "node:fs";
9
- import { dirname } from "node:path";
10
- import { mkdirSync } from "node:fs";
11
-
12
- export type RunnerConsoleReporter = (event: string, payload: Record<string, unknown>) => void;
13
-
14
- export type RunnerLogger = ((event: string, payload: Record<string, unknown>) => void) & {
15
- readonly close: () => Promise<void>;
16
- };
17
-
18
- export function createRunnerLogger(
19
- logFile: string,
20
- consoleReporter?: RunnerConsoleReporter | undefined,
21
- ): RunnerLogger {
22
- mkdirSync(dirname(logFile), { recursive: true });
23
- const fd = openSync(logFile, "a");
24
- const stream = createWriteStream("", { fd, flags: "a", autoClose: true });
25
-
26
- const logger = ((event: string, payload: Record<string, unknown>) => {
27
- stream.write(
28
- `${JSON.stringify({ ts: new Date().toISOString(), event, ...payload })}\n`,
29
- "utf8",
30
- );
31
- consoleReporter?.(event, payload);
32
- }) as RunnerLogger;
33
-
34
- Object.defineProperty(logger, "close", {
35
- value: () => closeStream(stream),
36
- });
37
-
38
- return logger;
39
- }
40
-
41
- function closeStream(stream: WriteStream): Promise<void> {
42
- if (stream.closed || stream.destroyed) {
43
- return Promise.resolve();
44
- }
45
-
46
- return new Promise((resolve, reject) => {
47
- stream.once("error", reject);
48
- stream.end(resolve);
49
- });
50
- }
@@ -1,129 +0,0 @@
1
- /*
2
- - Date: 2026-04-26
3
- Spec: plans/2026-04-26-local-codex-driver-worldclass-spec.md
4
- Relationship: Pure coalescing helpers for runner-batched Codex stream
5
- updates, keeping one logical Codex item mapped to one Kandan row while
6
- avoiding per-delta state churn in the session orchestrator.
7
- */
8
- import type {
9
- CodexAssistantDelta,
10
- CodexCommandOutputDelta,
11
- CodexFileChangeDelta,
12
- CodexReasoningDelta,
13
- } from "./codexOutput";
14
-
15
- type StreamDelta = {
16
- readonly itemKey: string;
17
- readonly turnId: string | undefined;
18
- };
19
-
20
- type TextDelta = StreamDelta & {
21
- readonly delta?: string | undefined;
22
- readonly patchText?: string | undefined;
23
- };
24
-
25
- export function coalesceAssistantDeltas(
26
- deltas: readonly CodexAssistantDelta[],
27
- ): CodexAssistantDelta[] {
28
- return coalesceTextDeltas(deltas, (delta) => ({
29
- itemKey: delta.itemKey,
30
- turnId: delta.turnId,
31
- delta: delta.delta,
32
- }));
33
- }
34
-
35
- export function coalesceReasoningDeltas(
36
- deltas: readonly CodexReasoningDelta[],
37
- ): CodexReasoningDelta[] {
38
- return coalesceTextDeltas(deltas, (delta) => ({
39
- itemKey: delta.itemKey,
40
- turnId: delta.turnId,
41
- delta: delta.delta,
42
- }));
43
- }
44
-
45
- export function coalesceFileChangeDeltas(
46
- deltas: readonly CodexFileChangeDelta[],
47
- ): CodexFileChangeDelta[] {
48
- return coalesceTextDeltas(deltas, (delta) => ({
49
- itemKey: delta.itemKey,
50
- turnId: delta.turnId,
51
- patchText: delta.patchText,
52
- }));
53
- }
54
-
55
- export function coalesceCommandOutputDeltas(
56
- deltas: readonly CodexCommandOutputDelta[],
57
- ): CodexCommandOutputDelta[] {
58
- const coalesced = new Map<string, CodexCommandOutputDelta>();
59
-
60
- for (const delta of deltas) {
61
- const key = commandOutputStreamingKey(delta);
62
- const existing = coalesced.get(key);
63
-
64
- if (existing === undefined) {
65
- coalesced.set(key, delta);
66
- continue;
67
- }
68
-
69
- coalesced.set(key, {
70
- ...delta,
71
- processId: delta.processId ?? existing.processId,
72
- delta: `${existing.delta}${delta.delta}`,
73
- });
74
- }
75
-
76
- return [...coalesced.values()];
77
- }
78
-
79
- export function firstDeltaTurnId(
80
- deltas: readonly { readonly turnId: string | undefined }[],
81
- ): string | undefined {
82
- return deltas.find((delta) => delta.turnId !== undefined)?.turnId;
83
- }
84
-
85
- function coalesceTextDeltas<TDelta extends TextDelta>(
86
- deltas: readonly TDelta[],
87
- clone: (delta: TDelta) => TDelta,
88
- ): TDelta[] {
89
- const coalesced = new Map<string, TDelta>();
90
-
91
- for (const delta of deltas) {
92
- const key = streamingItemKey(delta);
93
- const existing = coalesced.get(key);
94
-
95
- if (existing === undefined) {
96
- coalesced.set(key, clone(delta));
97
- continue;
98
- }
99
-
100
- coalesced.set(key, mergeTextDelta(existing, delta));
101
- }
102
-
103
- return [...coalesced.values()];
104
- }
105
-
106
- function mergeTextDelta<TDelta extends TextDelta>(
107
- existing: TDelta,
108
- incoming: TDelta,
109
- ): TDelta {
110
- if (existing.delta !== undefined || incoming.delta !== undefined) {
111
- return {
112
- ...incoming,
113
- delta: `${existing.delta ?? ""}${incoming.delta ?? ""}`,
114
- };
115
- }
116
-
117
- return {
118
- ...incoming,
119
- patchText: `${existing.patchText ?? ""}${incoming.patchText ?? ""}`,
120
- };
121
- }
122
-
123
- function streamingItemKey(delta: StreamDelta): string {
124
- return `${delta.turnId ?? "turn"}:${delta.itemKey}`;
125
- }
126
-
127
- function commandOutputStreamingKey(delta: CodexCommandOutputDelta): string {
128
- return `${streamingItemKey(delta)}:${delta.stream}`;
129
- }
@@ -1,102 +0,0 @@
1
- /*
2
- - Date: 2026-04-26
3
- Spec: plans/2026-04-26-local-codex-driver-worldclass-spec.md
4
- Relationship: Owns the shared timer/chain lifecycle for runner-batched Codex
5
- stream updates so assistant, reasoning, command-output, and file-change
6
- deltas all follow one flush state machine.
7
- */
8
-
9
- export type StreamDeltaQueue<TDelta extends { readonly turnId: string | undefined }> = {
10
- pending: TDelta[];
11
- timer: ReturnType<typeof setTimeout> | undefined;
12
- chain: Promise<void>;
13
- };
14
-
15
- export type StreamDeltaQueueRuntime<TDelta extends { readonly turnId: string | undefined }> = {
16
- readonly flushIntervalMs: number;
17
- readonly coalesce: (deltas: readonly TDelta[]) => TDelta[];
18
- readonly forward: (delta: TDelta) => Promise<void>;
19
- readonly firstTurnId: (deltas: readonly TDelta[]) => string | undefined;
20
- readonly logFailure: (turnId: string | undefined, error: unknown) => void;
21
- };
22
-
23
- export function createStreamDeltaQueue<
24
- TDelta extends { readonly turnId: string | undefined },
25
- >(): StreamDeltaQueue<TDelta> {
26
- return {
27
- pending: [],
28
- timer: undefined,
29
- chain: Promise.resolve(),
30
- };
31
- }
32
-
33
- export function enqueueStreamDelta<
34
- TDelta extends { readonly turnId: string | undefined },
35
- >(
36
- queue: StreamDeltaQueue<TDelta>,
37
- delta: TDelta,
38
- runtime: StreamDeltaQueueRuntime<TDelta>,
39
- ): void {
40
- queue.pending.push(delta);
41
- scheduleStreamDeltaFlush(queue, runtime);
42
- }
43
-
44
- export function flushStreamDeltaQueue<
45
- TDelta extends { readonly turnId: string | undefined },
46
- >(
47
- queue: StreamDeltaQueue<TDelta>,
48
- runtime: StreamDeltaQueueRuntime<TDelta>,
49
- ): void {
50
- clearStreamDeltaFlushTimer(queue);
51
-
52
- const pending = queue.pending;
53
-
54
- if (pending.length === 0) {
55
- return;
56
- }
57
-
58
- queue.pending = [];
59
- const deltas = runtime.coalesce(pending);
60
- const previous = queue.chain;
61
- const next = previous
62
- .catch(() => undefined)
63
- .then(async () => {
64
- for (const delta of deltas) {
65
- await runtime.forward(delta);
66
- }
67
- });
68
-
69
- queue.chain = next.catch(error => {
70
- runtime.logFailure(runtime.firstTurnId(deltas), error);
71
- });
72
- }
73
-
74
- export function clearStreamDeltaFlushTimer<
75
- TDelta extends { readonly turnId: string | undefined },
76
- >(queue: StreamDeltaQueue<TDelta>): void {
77
- if (queue.timer !== undefined) {
78
- clearTimeout(queue.timer);
79
- queue.timer = undefined;
80
- }
81
- }
82
-
83
- function scheduleStreamDeltaFlush<
84
- TDelta extends { readonly turnId: string | undefined },
85
- >(
86
- queue: StreamDeltaQueue<TDelta>,
87
- runtime: StreamDeltaQueueRuntime<TDelta>,
88
- ): void {
89
- if (queue.timer !== undefined) {
90
- return;
91
- }
92
-
93
- if (runtime.flushIntervalMs <= 0) {
94
- flushStreamDeltaQueue(queue, runtime);
95
- return;
96
- }
97
-
98
- queue.timer = setTimeout(() => {
99
- queue.timer = undefined;
100
- flushStreamDeltaQueue(queue, runtime);
101
- }, runtime.flushIntervalMs);
102
- }