@stigmer/cli 3.1.7 → 3.1.8
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/commands/share.d.ts +3 -0
- package/commands/share.d.ts.map +1 -0
- package/commands/share.js +75 -0
- package/commands/share.js.map +1 -0
- package/config/index.d.ts +1 -1
- package/config/index.d.ts.map +1 -1
- package/config/index.js +1 -1
- package/config/index.js.map +1 -1
- package/config/resolve.d.ts +19 -2
- package/config/resolve.d.ts.map +1 -1
- package/config/resolve.js +40 -2
- package/config/resolve.js.map +1 -1
- package/local/runtime/node.d.ts +9 -2
- package/local/runtime/node.d.ts.map +1 -1
- package/local/runtime/node.js +38 -23
- package/local/runtime/node.js.map +1 -1
- package/package.json +6 -6
- package/program.d.ts.map +1 -1
- package/program.js +2 -0
- package/program.js.map +1 -1
- package/resources/connect/oauth.d.ts +0 -9
- package/resources/connect/oauth.d.ts.map +1 -1
- package/resources/connect/oauth.js +1 -17
- package/resources/connect/oauth.js.map +1 -1
- package/resources/share.d.ts +37 -0
- package/resources/share.d.ts.map +1 -0
- package/resources/share.js +144 -0
- package/resources/share.js.map +1 -0
- package/resources/stream/convert.d.ts +6 -2
- package/resources/stream/convert.d.ts.map +1 -1
- package/resources/stream/convert.js +8 -2
- package/resources/stream/convert.js.map +1 -1
- package/resources/stream/events.d.ts +14 -2
- package/resources/stream/events.d.ts.map +1 -1
- package/resources/stream/events.js.map +1 -1
- package/resources/stream/render-ndjson.js +2 -0
- package/resources/stream/render-ndjson.js.map +1 -1
- package/resources/stream/render-plaintext.d.ts.map +1 -1
- package/resources/stream/render-plaintext.js +3 -0
- package/resources/stream/render-plaintext.js.map +1 -1
- package/resources/stream/snapshot.js +6 -1
- package/resources/stream/snapshot.js.map +1 -1
- package/resources/stream/tool-state.d.ts +1 -1
- package/resources/stream/tool-state.d.ts.map +1 -1
- package/resources/stream/tool-state.js +13 -4
- package/resources/stream/tool-state.js.map +1 -1
- package/src/commands/connect.test.ts +26 -4
- package/src/commands/share.ts +99 -0
- package/src/config/index.ts +2 -0
- package/src/config/resolve.test.ts +38 -0
- package/src/config/resolve.ts +42 -3
- package/src/local/runtime/node.ts +43 -25
- package/src/local/runtime/runtime.test.ts +5 -4
- package/src/program.test.ts +7 -0
- package/src/program.ts +2 -0
- package/src/resources/connect/oauth.test.ts +1 -24
- package/src/resources/connect/oauth.ts +1 -17
- package/src/resources/read-verbs.integration.test.ts +1 -1
- package/src/resources/share.test.ts +389 -0
- package/src/resources/share.ts +227 -0
- package/src/resources/stream/convert.ts +17 -3
- package/src/resources/stream/diff.test.ts +22 -0
- package/src/resources/stream/events.ts +15 -1
- package/src/resources/stream/render-ndjson.test.ts +10 -0
- package/src/resources/stream/render-ndjson.ts +2 -0
- package/src/resources/stream/render-plaintext.test.ts +8 -0
- package/src/resources/stream/render-plaintext.ts +3 -0
- package/src/resources/stream/snapshot.test.ts +14 -0
- package/src/resources/stream/snapshot.ts +5 -1
- package/src/resources/stream/tool-state.ts +13 -4
|
@@ -15,7 +15,7 @@ import type { JsonObject } from "@bufbuild/protobuf";
|
|
|
15
15
|
export interface ToolCallInfo {
|
|
16
16
|
readonly id: string;
|
|
17
17
|
readonly name: string;
|
|
18
|
-
/** Mapped status: pending|running|completed|failed|waiting_approval|skipped|unknown. */
|
|
18
|
+
/** Mapped status: pending|running|completed|failed|waiting_approval|skipped|interrupted|unknown. */
|
|
19
19
|
readonly status: string;
|
|
20
20
|
readonly args?: JsonObject;
|
|
21
21
|
readonly result: string;
|
|
@@ -85,6 +85,19 @@ export interface ToolCompletedEvent {
|
|
|
85
85
|
readonly subAgentId: string;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* A tool call the platform settled to INTERRUPTED because its execution
|
|
90
|
+
* terminalized mid-call (issue #207). A distinct terminal event — routing it
|
|
91
|
+
* through toolCompleted would print a dishonest checkmark for a tool that
|
|
92
|
+
* never finished.
|
|
93
|
+
*/
|
|
94
|
+
export interface ToolInterruptedEvent {
|
|
95
|
+
readonly kind: "toolInterrupted";
|
|
96
|
+
readonly toolCallId: string;
|
|
97
|
+
readonly toolCall: ToolCallInfo;
|
|
98
|
+
readonly subAgentId: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
88
101
|
export interface ToolWaitingApprovalEvent {
|
|
89
102
|
readonly kind: "toolWaitingApproval";
|
|
90
103
|
readonly toolCallId: string;
|
|
@@ -175,6 +188,7 @@ export type StreamEvent =
|
|
|
175
188
|
| ToolResultEvent
|
|
176
189
|
| ToolRunningEvent
|
|
177
190
|
| ToolCompletedEvent
|
|
191
|
+
| ToolInterruptedEvent
|
|
178
192
|
| ToolWaitingApprovalEvent
|
|
179
193
|
| ToolStreamDeltaEvent
|
|
180
194
|
| SystemMessageEvent
|
|
@@ -62,6 +62,16 @@ describe("envelope", () => {
|
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
describe("tool events", () => {
|
|
65
|
+
it("emits tool_interrupted as its own wire type (issue #207)", () => {
|
|
66
|
+
const { data } = render([
|
|
67
|
+
{ kind: "toolInterrupted", toolCallId: "t1", subAgentId: "", toolCall: toolInfo({ id: "t1", name: "shell", status: "interrupted" }) },
|
|
68
|
+
]);
|
|
69
|
+
expect(data.lines()[0]).toMatchObject({
|
|
70
|
+
type: "tool_interrupted",
|
|
71
|
+
payload: { tool_call_id: "t1", tool_name: "shell", status: "interrupted" },
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
65
75
|
it("emits tool_running with only the meaningful fields", () => {
|
|
66
76
|
const { data } = render([
|
|
67
77
|
{ kind: "toolRunning", toolCallId: "t1", subAgentId: "", toolCall: toolInfo({ id: "t1", result: "out", durationMs: 42 }) },
|
|
@@ -77,6 +77,8 @@ function mapToNdjson(event: StreamEvent): MappedEvent | undefined {
|
|
|
77
77
|
return { type: "tool_running", payload: toolEventPayload(event.toolCallId, event.toolCall, event.subAgentId) };
|
|
78
78
|
case "toolCompleted":
|
|
79
79
|
return { type: "tool_completed", payload: toolEventPayload(event.toolCallId, event.toolCall, event.subAgentId) };
|
|
80
|
+
case "toolInterrupted":
|
|
81
|
+
return { type: "tool_interrupted", payload: toolEventPayload(event.toolCallId, event.toolCall, event.subAgentId) };
|
|
80
82
|
case "toolWaitingApproval":
|
|
81
83
|
return {
|
|
82
84
|
type: "tool_waiting_approval",
|
|
@@ -39,6 +39,14 @@ describe("PlaintextRenderer", () => {
|
|
|
39
39
|
expect(status).toContain("✗ shell: exploded");
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
+
it("renders an interrupted tool with the neutral glyph, not a checkmark (issue #207)", () => {
|
|
43
|
+
const { status } = render([
|
|
44
|
+
{ kind: "toolInterrupted", toolCallId: "t1", subAgentId: "", toolCall: toolInfo({ name: "shell", status: "interrupted" }) },
|
|
45
|
+
]);
|
|
46
|
+
expect(status).toContain("⊘ shell (interrupted)");
|
|
47
|
+
expect(status).not.toContain("✓");
|
|
48
|
+
});
|
|
49
|
+
|
|
42
50
|
it("notes the auto-skip for approvals and resolves to SKIP", () => {
|
|
43
51
|
const r = new PlaintextRenderer({ data: { write: () => {} }, status: { write: () => {} } });
|
|
44
52
|
const action = r.resolveApproval({
|
|
@@ -49,6 +49,9 @@ export class PlaintextRenderer implements HeadlessRenderer {
|
|
|
49
49
|
: ` ✓ ${event.toolCall.name}\n`,
|
|
50
50
|
);
|
|
51
51
|
return;
|
|
52
|
+
case "toolInterrupted":
|
|
53
|
+
status.write(` ⊘ ${event.toolCall.name} (interrupted)\n`);
|
|
54
|
+
return;
|
|
52
55
|
case "approvalNeeded":
|
|
53
56
|
status.write(` ⚠ Approval needed: ${event.toolName} (auto-skipped in non-TTY mode)\n`);
|
|
54
57
|
return;
|
|
@@ -79,6 +79,20 @@ describe("snapshotToEvents", () => {
|
|
|
79
79
|
expect(events[0]).toMatchObject({ kind: "toolCompleted", toolCallId: "tc1" });
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
+
it("replays a platform-interrupted call as toolInterrupted — never a live running block", () => {
|
|
83
|
+
// Issue #207: before the fix an INTERRUPTED status fell through to
|
|
84
|
+
// "unknown" (non-terminal) and replayed as toolRunning, a block that never
|
|
85
|
+
// closes. It must replay as its own settled event.
|
|
86
|
+
const events = snapshotToEvents([
|
|
87
|
+
exec({
|
|
88
|
+
phase: ExecutionPhase.EXECUTION_CANCELLED,
|
|
89
|
+
messages: [toolMsg(tool("tc1", ToolCallStatus.TOOL_CALL_INTERRUPTED))],
|
|
90
|
+
}),
|
|
91
|
+
]);
|
|
92
|
+
expect(kinds(events)).toEqual(["toolInterrupted", "done"]);
|
|
93
|
+
expect(events[0]).toMatchObject({ kind: "toolInterrupted", toolCallId: "tc1" });
|
|
94
|
+
});
|
|
95
|
+
|
|
82
96
|
it("only the final execution emits done", () => {
|
|
83
97
|
const events = snapshotToEvents([
|
|
84
98
|
exec({ message: "first", messages: [aiMsg("a")] }),
|
|
@@ -159,7 +159,11 @@ function emitOtherMessage(out: StreamEvent[], msg: AgentMessage): void {
|
|
|
159
159
|
function emitToolByStatus(out: StreamEvent[], tc: ToolCall): void {
|
|
160
160
|
const status = mapToolCallStatus(tc.status);
|
|
161
161
|
const toolCall = convertToolCall(tc);
|
|
162
|
-
if (
|
|
162
|
+
if (status === "interrupted") {
|
|
163
|
+
// Terminal but never "completed": replaying a platform-interrupted call as
|
|
164
|
+
// either a checkmark or a live running block would lie (issue #207).
|
|
165
|
+
out.push({ kind: "toolInterrupted", toolCallId: tc.id, toolCall, subAgentId: "" });
|
|
166
|
+
} else if (isTerminalToolStatus(status)) {
|
|
163
167
|
out.push({ kind: "toolCompleted", toolCallId: tc.id, toolCall, subAgentId: "" });
|
|
164
168
|
} else if (status === "waiting_approval") {
|
|
165
169
|
out.push({ kind: "toolWaitingApproval", toolCallId: tc.id, toolCall, subAgentId: "" });
|
|
@@ -55,14 +55,14 @@ export class ToolStateTracker {
|
|
|
55
55
|
// First sight, already terminal (e.g. re-attach): synthesize completion so
|
|
56
56
|
// the block exists — otherwise its MESSAGE_TOOL is suppressed and it vanishes.
|
|
57
57
|
if (!seen && isTerminalToolStatus(current)) {
|
|
58
|
-
pending.push(this.
|
|
58
|
+
pending.push(this.terminal(tc, current, subAgentId));
|
|
59
59
|
this.states.set(tc.id, current);
|
|
60
60
|
continue;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
// running/waiting -> terminal: close the block.
|
|
64
64
|
if (seen && (prev === "running" || prev === "waiting_approval") && isTerminalToolStatus(current)) {
|
|
65
|
-
pending.push(this.
|
|
65
|
+
pending.push(this.terminal(tc, current, subAgentId));
|
|
66
66
|
this.states.set(tc.id, current);
|
|
67
67
|
this.results.delete(tc.id);
|
|
68
68
|
continue;
|
|
@@ -113,8 +113,16 @@ export class ToolStateTracker {
|
|
|
113
113
|
return { kind: "toolWaitingApproval", toolCallId: tc.id, toolCall: convertToolCall(tc), subAgentId };
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
// The terminal-close event for a settled status: interrupted gets its own
|
|
117
|
+
// kind (issue #207) so renderers never print a checkmark for a tool the
|
|
118
|
+
// platform cut short; every other terminal status closes as completed.
|
|
119
|
+
private terminal(tc: ToolCall, status: string, subAgentId: string): StreamEvent {
|
|
120
|
+
return {
|
|
121
|
+
kind: status === "interrupted" ? "toolInterrupted" : "toolCompleted",
|
|
122
|
+
toolCallId: tc.id,
|
|
123
|
+
toolCall: convertToolCall(tc),
|
|
124
|
+
subAgentId,
|
|
125
|
+
};
|
|
118
126
|
}
|
|
119
127
|
}
|
|
120
128
|
|
|
@@ -123,6 +131,7 @@ export function toolEventId(event: StreamEvent): string {
|
|
|
123
131
|
switch (event.kind) {
|
|
124
132
|
case "toolRunning":
|
|
125
133
|
case "toolCompleted":
|
|
134
|
+
case "toolInterrupted":
|
|
126
135
|
case "toolWaitingApproval":
|
|
127
136
|
case "toolStreamDelta":
|
|
128
137
|
return event.toolCallId;
|