gentle-pi 2.5.0 → 2.6.0
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 +139 -30
- package/assets/agents/gentle-ai-worker.md +4 -0
- package/assets/agents/jd-fix-agent.md +18 -0
- package/assets/agents/jd-judge-a.md +1 -1
- package/assets/agents/jd-judge-b.md +1 -1
- package/assets/agents/sdd-apply.md +7 -5
- package/assets/agents/sdd-archive.md +5 -3
- package/assets/agents/sdd-design.md +4 -0
- package/assets/agents/sdd-explore.md +4 -0
- package/assets/agents/sdd-init.md +4 -0
- package/assets/agents/sdd-onboard.md +4 -0
- package/assets/agents/sdd-proposal.md +4 -0
- package/assets/agents/sdd-remediate.md +37 -0
- package/assets/agents/sdd-research.md +26 -3
- package/assets/agents/sdd-spec.md +4 -0
- package/assets/agents/sdd-status.md +9 -75
- package/assets/agents/sdd-sync.md +4 -0
- package/assets/agents/sdd-tasks.md +4 -0
- package/assets/agents/sdd-verify.md +5 -3
- package/assets/chains/sdd-full.chain.md +4 -0
- package/assets/chains/sdd-plan.chain.md +4 -0
- package/assets/chains/sdd-verify.chain.md +4 -0
- package/assets/migrations/managed-assets-v2.5.0.json +7 -0
- package/assets/orchestrator-delegation.md +21 -3
- package/assets/sdd-orchestrator-workflow.md +54 -21
- package/assets/support/sdd-status-contract.md +34 -90
- package/contracts/telemetry/runtime-aggregate-v1.schema.json +65 -0
- package/docs/telemetry.md +57 -1
- package/docs/windows-startup-console-visibility.md +18 -0
- package/extensions/ask-user-choice.ts +143 -15
- package/extensions/codegraph-tools.ts +1 -0
- package/extensions/gentle-agents.ts +799 -50
- package/extensions/gentle-ai.ts +2033 -322
- package/extensions/gentle-shell.ts +145 -42
- package/extensions/gentle-todo.ts +47 -12
- package/extensions/quiet-tools.ts +1 -0
- package/extensions/runtime-metrics.ts +130 -0
- package/extensions/sdd-init.ts +2 -2
- package/extensions/startup-banner.ts +52 -75
- package/lib/agent-profiles.ts +550 -0
- package/lib/agents-completion-delivery.ts +72 -0
- package/lib/agents-config.ts +7 -10
- package/lib/agents-history.ts +9 -1
- package/lib/agents-messaging.ts +187 -0
- package/lib/agents-protocol.ts +77 -5
- package/lib/agents-runner.ts +548 -26
- package/lib/agents-thread-view.ts +57 -0
- package/lib/agents-view-layout.ts +40 -0
- package/lib/agents-view.ts +548 -191
- package/lib/agents-widget.ts +33 -14
- package/lib/gentle-ai-binary.ts +3 -1
- package/lib/gentle-ai-renderer.ts +8 -6
- package/lib/native-review-cli.ts +283 -1
- package/lib/orchestrator-presence.ts +337 -0
- package/lib/profiles-orchestrator.ts +203 -0
- package/lib/review-candidate-view-owner.ts +296 -46
- package/lib/review-candidate-view.ts +30 -20
- package/lib/review-consent-component.ts +247 -0
- package/lib/review-consent-ui.ts +53 -8
- package/lib/review-host-relay.ts +28 -0
- package/lib/review-integration-v2.ts +187 -5
- package/lib/review-last-event-controller.ts +7 -4
- package/lib/review-reminder-receipt.ts +74 -0
- package/lib/review-session-standing-permission.ts +27 -6
- package/lib/runtime-metrics-children.ts +199 -0
- package/lib/runtime-metrics-delivery.ts +68 -0
- package/lib/runtime-metrics-native.ts +166 -0
- package/lib/runtime-metrics-pi-identity.ts +113 -0
- package/lib/runtime-metrics-policy.ts +51 -0
- package/lib/runtime-metrics.ts +255 -0
- package/lib/sdd-preflight.ts +362 -81
- package/lib/sdd-research-capabilities.ts +228 -0
- package/lib/sdd-status.ts +29 -7
- package/lib/session-worktree-registry.ts +118 -0
- package/lib/shell-bar.ts +47 -1
- package/lib/shell-card.ts +1 -4
- package/lib/shell-changes-view.ts +362 -37
- package/lib/shell-changes.ts +81 -1
- package/lib/shell-prompt.ts +11 -15
- package/lib/shell-sidebar-banner.ts +11 -0
- package/lib/shell-sidebar-layout.ts +213 -0
- package/lib/shell-sidebar.ts +41 -0
- package/lib/shell-todo.ts +28 -11
- package/lib/telemetry-trigger.ts +2 -0
- package/package.json +6 -3
- package/runtime/gentle-ai-binary.mjs +3 -1
- package/runtime/native-review-cli.mjs +283 -1
- package/runtime/review-integration-v2.mjs +187 -5
- package/runtime/telemetry-trigger.mjs +2 -0
- package/scripts/build-runtime-modules.mjs +9 -1
- package/scripts/check-types.mjs +125 -0
- package/scripts/gentle-ai-installer.mjs +10 -10
- package/scripts/install-gentle-ai.mjs +12 -0
- package/scripts/install-tui-mode-setting.mjs +114 -0
- package/scripts/test-packed-runner.mjs +16 -2
- package/scripts/types-baseline.json +99 -0
- package/scripts/verify-package-files.mjs +4 -2
- package/skills/_shared/review-ledger-contract.md +17 -1
- package/skills/issue-creation/SKILL.md +3 -3
- package/skills/judgment-day/SKILL.md +17 -3
- package/skills/judgment-day/references/prompts-and-formats.md +14 -3
- package/tests/agent-profiles.test.ts +722 -0
- package/tests/agents-completion-delivery.test.ts +94 -0
- package/tests/agents-config.test.ts +62 -0
- package/tests/agents-fake-child.ts +15 -1
- package/tests/agents-grouping.test.ts +179 -0
- package/tests/agents-integration.test.ts +100 -0
- package/tests/agents-messaging.test.ts +94 -0
- package/tests/agents-protocol.test.ts +45 -0
- package/tests/agents-queries.test.ts +190 -0
- package/tests/agents-responsive.test.ts +43 -0
- package/tests/agents-runner.test.ts +571 -14
- package/tests/agents-thread-view.test.ts +45 -0
- package/tests/agents-view.test.ts +476 -65
- package/tests/agents-widget.test.ts +31 -1
- package/tests/artifact-language.test.ts +25 -2
- package/tests/ask-user-choice.test.ts +169 -3
- package/tests/asset-installation-runtime.test.ts +108 -0
- package/tests/autonomous-guard.test.ts +116 -1
- package/tests/codegraph-tools.test.ts +2 -1
- package/tests/delegated-key-learnings-contract.test.ts +1 -1
- package/tests/devbinary/native-review-parity.devtest.ts +2 -0
- package/tests/feature-request-form.test.ts +67 -0
- package/tests/fixtures/agents-messaging-child.mjs +5 -0
- package/tests/fixtures/runtime-metrics-native-batches.json +6 -0
- package/tests/gentle-agents.test.ts +1460 -33
- package/tests/gentle-ai-binary.test.ts +7 -2
- package/tests/gentle-ai-installer.test.ts +47 -47
- package/tests/gentle-ai-renderer.test.ts +38 -0
- package/tests/gentle-ai.test.ts +944 -4
- package/tests/gentle-shell.test.ts +303 -12
- package/tests/gentle-todo.test.ts +54 -10
- package/tests/install-tui-mode-setting.test.ts +324 -0
- package/tests/issue-creation-skill.test.ts +22 -0
- package/tests/model-routing-authority.test.ts +12 -0
- package/tests/native-review-capability-contract.test.ts +12 -1
- package/tests/native-review-cli.test.ts +277 -3
- package/tests/native-review-parity.test.ts +14 -7
- package/tests/native-sdd-attempt-authority.test.ts +7 -2
- package/tests/orchestrator-presence.test.ts +389 -0
- package/tests/package-manifest.test.ts +232 -7
- package/tests/profiles-orchestrator.test.ts +208 -0
- package/tests/quiet-tool-rendering.test.ts +1 -0
- package/tests/rdd-aware-verification-contract.test.ts +10 -0
- package/tests/review-agent-end-preflight.test.ts +332 -24
- package/tests/review-candidate-view.test.ts +304 -6
- package/tests/review-consent-ui.test.ts +352 -0
- package/tests/review-contract-prompt.test.ts +14 -0
- package/tests/review-controller-native-routing.test.ts +563 -3
- package/tests/review-controller.test.ts +1 -1
- package/tests/review-host-relay-restart-parity.test.ts +142 -1
- package/tests/review-host-relay-routing.test.ts +364 -4
- package/tests/review-host-relay.test.ts +29 -0
- package/tests/review-integration-v2-forward.test.ts +44 -0
- package/tests/review-integration-v2.test.ts +164 -0
- package/tests/review-last-event-closure.test.ts +105 -1
- package/tests/review-ledger-contract.test.ts +61 -6
- package/tests/review-reminder-receipt.test.ts +62 -0
- package/tests/review-session-standing-permission-controller.test.ts +52 -4
- package/tests/review-session-standing-permission.test.ts +30 -0
- package/tests/runtime-harness.mjs +447 -39
- package/tests/runtime-metrics-children.test.ts +206 -0
- package/tests/runtime-metrics-delivery.test.ts +85 -0
- package/tests/runtime-metrics-extension.test.ts +187 -0
- package/tests/runtime-metrics-native.test.ts +209 -0
- package/tests/runtime-metrics-pi-identity.test.ts +113 -0
- package/tests/runtime-metrics-policy.test.ts +62 -0
- package/tests/runtime-metrics.test.ts +184 -0
- package/tests/sdd-agent-tools.test.ts +10 -1
- package/tests/sdd-execution-routing-contract.test.ts +28 -0
- package/tests/sdd-managed-runtime-settlement.test.ts +331 -0
- package/tests/sdd-native-managed-uptake.test.ts +253 -0
- package/tests/sdd-planning-routing-contract.test.ts +45 -0
- package/tests/sdd-preflight.test.ts +252 -8
- package/tests/sdd-research-capabilities.test.ts +256 -0
- package/tests/sdd-research-live.test.ts +241 -0
- package/tests/sdd-selection-transport.test.ts +504 -0
- package/tests/sdd-status.test.ts +51 -0
- package/tests/session-worktree-registry.test.ts +135 -0
- package/tests/shell-card.test.ts +24 -3
- package/tests/shell-changes-view.test.ts +471 -8
- package/tests/shell-changes.test.ts +168 -0
- package/tests/shell-prompt.test.ts +28 -6
- package/tests/shell-sidebar-banner.test.ts +23 -0
- package/tests/shell-sidebar-layout.test.ts +387 -0
- package/tests/shell-sidebar.test.ts +50 -0
- package/tests/shell-todo.test.ts +100 -11
- package/tests/startup-banner.test.ts +126 -0
- package/tests/telemetry-trigger.test.ts +3 -1
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
export const CHILD_MESSAGE_MAX_BYTES = 8 * 1024;
|
|
2
|
+
export const CHILD_ACK_ERROR_MAX_BYTES = 256;
|
|
3
|
+
export const CHILD_QUERY_ERROR_MAX_BYTES = 256;
|
|
4
|
+
export const CHILD_MESSAGE_MAX_INFLIGHT = 8;
|
|
5
|
+
export const CHILD_MESSAGE_TIMEOUT_MS = 2_000;
|
|
6
|
+
export const CHILD_QUERY_MAX_INFLIGHT = 4;
|
|
7
|
+
export const CHILD_QUERY_TIMEOUT_MS = 30_000;
|
|
8
|
+
|
|
9
|
+
export interface NotificationFrame {
|
|
10
|
+
id: string;
|
|
11
|
+
kind: "notification";
|
|
12
|
+
message: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface QueryFrame {
|
|
16
|
+
id: string;
|
|
17
|
+
kind: "query";
|
|
18
|
+
message: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type ChildFrame = NotificationFrame | QueryFrame;
|
|
22
|
+
|
|
23
|
+
interface AckFrame {
|
|
24
|
+
id: string;
|
|
25
|
+
accepted: boolean;
|
|
26
|
+
error?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type ReplyFrame = { id: string; message: string } | { id: string; error: string };
|
|
30
|
+
|
|
31
|
+
export interface IpcEndpoint {
|
|
32
|
+
connected?: boolean;
|
|
33
|
+
send(message: unknown, callback?: (error: Error | null) => void): boolean;
|
|
34
|
+
on(event: "message" | "disconnect", listener: (...args: unknown[]) => void): unknown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type Cancel = () => void;
|
|
38
|
+
type Schedule = (fn: () => void, ms: number) => Cancel;
|
|
39
|
+
type Pending<T> = { resolve: (value: T) => void; reject: (error: Error) => void; cancel: Cancel };
|
|
40
|
+
|
|
41
|
+
function record(value: unknown): value is Record<string, unknown> {
|
|
42
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function exact(value: Record<string, unknown>, keys: readonly string[]): boolean {
|
|
46
|
+
return Object.keys(value).every((key) => keys.includes(key));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function wellFormedUnicode(value: string): boolean {
|
|
50
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
51
|
+
const code = value.charCodeAt(index);
|
|
52
|
+
if (code >= 0xD800 && code <= 0xDBFF) {
|
|
53
|
+
const next = value.charCodeAt(index + 1);
|
|
54
|
+
if (index + 1 >= value.length || next < 0xDC00 || next > 0xDFFF) return false;
|
|
55
|
+
index += 1;
|
|
56
|
+
} else if (code >= 0xDC00 && code <= 0xDFFF) return false;
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function boundedText(value: unknown, maxBytes: number): value is string {
|
|
62
|
+
return typeof value === "string" && wellFormedUnicode(value) && Buffer.byteLength(value, "utf8") <= maxBytes;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function validCorrelation(value: unknown, prefix: "n" | "q"): value is string {
|
|
66
|
+
if (typeof value !== "string" || !new RegExp(`^${prefix}[1-9]\\d{0,15}$`).test(value)) return false;
|
|
67
|
+
const counter = Number(value.slice(1));
|
|
68
|
+
return Number.isSafeInteger(counter) && counter > 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function validChildId(value: unknown): value is string {
|
|
72
|
+
return validCorrelation(value, "n");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function validChildQueryId(value: unknown): value is string {
|
|
76
|
+
return validCorrelation(value, "q");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function validChildMessage(value: unknown): value is string {
|
|
80
|
+
return boundedText(value, CHILD_MESSAGE_MAX_BYTES);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function parseChildFrame(value: unknown): { frame?: ChildFrame; id?: string; error?: string } {
|
|
84
|
+
if (!record(value)) return { error: "invalid child IPC frame" };
|
|
85
|
+
const id = validChildQueryId(value.id) || validChildId(value.id) ? value.id : undefined;
|
|
86
|
+
if (!exact(value, ["id", "kind", "message"])) return { ...(id === undefined ? {} : { id }), error: "invalid child IPC frame" };
|
|
87
|
+
if (id === undefined) return { error: "invalid child IPC correlation" };
|
|
88
|
+
if (value.kind !== "notification" && value.kind !== "query") return { id, error: "unsupported child IPC kind" };
|
|
89
|
+
if (value.kind === "notification" && !validChildId(id) || value.kind === "query" && !validChildQueryId(id)) return { id, error: "invalid child IPC correlation" };
|
|
90
|
+
if (!validChildMessage(value.message)) return { id, error: "invalid child IPC message" };
|
|
91
|
+
return { frame: { id, kind: value.kind, message: value.message } } as { frame: ChildFrame };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function parseAck(value: unknown): AckFrame | undefined {
|
|
95
|
+
if (!record(value) || !exact(value, ["id", "kind", "accepted", "error"]) || !validChildId(value.id) || value.kind !== "ack" || typeof value.accepted !== "boolean") return undefined;
|
|
96
|
+
if (value.accepted && value.error !== undefined) return undefined;
|
|
97
|
+
if (value.error !== undefined && !boundedText(value.error, CHILD_ACK_ERROR_MAX_BYTES)) return undefined;
|
|
98
|
+
return { id: value.id, accepted: value.accepted, ...(value.error === undefined ? {} : { error: value.error }) };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function parseReply(value: unknown): ReplyFrame | undefined {
|
|
102
|
+
if (!record(value) || !validChildQueryId(value.id) || value.kind !== "reply") return undefined;
|
|
103
|
+
if (exact(value, ["id", "kind", "message"]) && validChildMessage(value.message)) return { id: value.id, message: value.message };
|
|
104
|
+
if (exact(value, ["id", "kind", "error"]) && boundedText(value.error, CHILD_QUERY_ERROR_MAX_BYTES)) return { id: value.id, error: value.error };
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Child-side admissions make acceptance explicit, but do not imply the parent
|
|
109
|
+
// model read the message or that any later delivery is guaranteed.
|
|
110
|
+
export class ChildMessenger {
|
|
111
|
+
private readonly pending = new Map<string, Pending<void>>();
|
|
112
|
+
private readonly queries = new Map<string, Pending<string>>();
|
|
113
|
+
private readonly endpoint: IpcEndpoint;
|
|
114
|
+
private readonly schedule: Schedule;
|
|
115
|
+
private nextId = 0;
|
|
116
|
+
private nextQueryId = 0;
|
|
117
|
+
private closed = false;
|
|
118
|
+
|
|
119
|
+
constructor(endpoint: IpcEndpoint, schedule: Schedule = (fn, ms) => {
|
|
120
|
+
const timer = setTimeout(fn, ms);
|
|
121
|
+
timer.unref?.();
|
|
122
|
+
return () => clearTimeout(timer);
|
|
123
|
+
}) {
|
|
124
|
+
this.endpoint = endpoint;
|
|
125
|
+
this.schedule = schedule;
|
|
126
|
+
endpoint.on("message", (value) => this.receive(value));
|
|
127
|
+
endpoint.on("disconnect", () => this.close("parent notification channel closed"));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
notify(message: string): Promise<void> {
|
|
131
|
+
if (!this.available()) return Promise.reject(new Error("parent notification channel closed"));
|
|
132
|
+
if (!validChildMessage(message)) return Promise.reject(new Error("message is not well-formed UTF-16 or exceeds 8KiB"));
|
|
133
|
+
if (this.pending.size >= CHILD_MESSAGE_MAX_INFLIGHT) return Promise.reject(new Error("too many pending parent-message admissions"));
|
|
134
|
+
if (this.nextId >= Number.MAX_SAFE_INTEGER) return Promise.reject(new Error("parent-message correlation exhausted"));
|
|
135
|
+
return this.send(this.pending, `n${++this.nextId}`, "notification", message, CHILD_MESSAGE_TIMEOUT_MS, "parent-message admission timed out");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
query(message: string): Promise<string> {
|
|
139
|
+
if (!this.available()) return Promise.reject(new Error("parent notification channel closed"));
|
|
140
|
+
if (!validChildMessage(message)) return Promise.reject(new Error("query is not well-formed UTF-16 or exceeds 8KiB"));
|
|
141
|
+
if (this.queries.size >= CHILD_QUERY_MAX_INFLIGHT) return Promise.reject(new Error("too many pending parent queries"));
|
|
142
|
+
if (this.nextQueryId >= Number.MAX_SAFE_INTEGER) return Promise.reject(new Error("parent query correlation exhausted"));
|
|
143
|
+
return this.send(this.queries, `q${++this.nextQueryId}`, "query", message, CHILD_QUERY_TIMEOUT_MS, "parent query timed out");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
close(reason = "parent notification channel closed"): void {
|
|
147
|
+
if (this.closed) return;
|
|
148
|
+
this.closed = true;
|
|
149
|
+
for (const id of [...this.pending.keys()]) this.settle(this.pending, id, new Error(reason));
|
|
150
|
+
for (const id of [...this.queries.keys()]) this.settle(this.queries, id, new Error(reason));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private available(): boolean {
|
|
154
|
+
if (!this.closed && this.endpoint.connected !== false) return true;
|
|
155
|
+
this.close("parent notification channel closed");
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private send<T>(pending: Map<string, Pending<T>>, id: string, kind: "notification" | "query", message: string, timeout: number, timeoutMessage: string): Promise<T> {
|
|
160
|
+
return new Promise((resolve, reject) => {
|
|
161
|
+
const settle = (error?: Error, reply?: T) => this.settle(pending, id, error, reply);
|
|
162
|
+
const cancel = this.schedule(() => settle(new Error(timeoutMessage)), timeout);
|
|
163
|
+
pending.set(id, { resolve, reject, cancel });
|
|
164
|
+
try { this.endpoint.send({ id, kind, message }, (error) => { if (error) settle(error); }); }
|
|
165
|
+
catch (error) { settle(error instanceof Error ? error : new Error(String(error))); }
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private receive(value: unknown): void {
|
|
170
|
+
const ack = parseAck(value);
|
|
171
|
+
if (ack) {
|
|
172
|
+
this.settle(this.pending, ack.id, ack.accepted ? undefined : new Error(ack.error || "parent rejected notification"));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const reply = parseReply(value);
|
|
176
|
+
if (reply) this.settle(this.queries, reply.id, "error" in reply ? new Error(reply.error || "parent rejected query") : undefined, "message" in reply ? reply.message : undefined);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private settle<T>(pending: Map<string, Pending<T>>, id: string, error?: Error, value?: T): void {
|
|
180
|
+
const entry = pending.get(id);
|
|
181
|
+
if (!entry) return;
|
|
182
|
+
pending.delete(id);
|
|
183
|
+
entry.cancel();
|
|
184
|
+
if (error) entry.reject(error);
|
|
185
|
+
else entry.resolve(value as T);
|
|
186
|
+
}
|
|
187
|
+
}
|
package/lib/agents-protocol.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { RemediationObservations, RemediationScope } from "./agents-runner.ts";
|
|
2
|
+
import type { NativeSddAcquireRequest, NativeSddSettleRequest, NativeSddAttemptResult } from "./native-review-cli.ts";
|
|
1
3
|
import { sanitizeTerminalText } from "./terminal-theme.ts";
|
|
2
4
|
|
|
3
5
|
// Gentle Agents protocol. A child pi process streams RPC events; the host
|
|
@@ -32,6 +34,7 @@ export const TASK_EVENT = {
|
|
|
32
34
|
ASK: "ask",
|
|
33
35
|
NOTE: "note",
|
|
34
36
|
USAGE: "usage",
|
|
37
|
+
RESPONSE_OBSERVATION: "response_observation",
|
|
35
38
|
} as const;
|
|
36
39
|
|
|
37
40
|
export type TaskEventType = (typeof TASK_EVENT)[keyof typeof TASK_EVENT];
|
|
@@ -69,7 +72,24 @@ export interface AskEvent { type: typeof TASK_EVENT.ASK; request: AskRequest }
|
|
|
69
72
|
export interface NoteEvent { type: typeof TASK_EVENT.NOTE; text: string }
|
|
70
73
|
export interface UsageEvent { type: typeof TASK_EVENT.USAGE; tokens: number; cost: number }
|
|
71
74
|
|
|
72
|
-
export type
|
|
75
|
+
export type ChildUnavailable = Readonly<{ state: "unavailable" }>;
|
|
76
|
+
export type ChildMetadata = Readonly<{ state: "observed"; value: string }> | ChildUnavailable;
|
|
77
|
+
export type ChildTokenMeasurement = Readonly<{ state: "reported"; value: number }> | ChildUnavailable;
|
|
78
|
+
export interface ChildResponseObservation {
|
|
79
|
+
/** SDK message metadata, not authenticated route or selected-model evidence. */
|
|
80
|
+
readonly provider: ChildMetadata;
|
|
81
|
+
readonly model: ChildMetadata;
|
|
82
|
+
readonly responseModel: ChildMetadata;
|
|
83
|
+
/** Exact provider-native effort when supplied, NOT Pi's selected effort. */
|
|
84
|
+
readonly providerThinkingLevel: ChildMetadata;
|
|
85
|
+
readonly selected: Readonly<{ provider: ChildUnavailable; model: ChildUnavailable; effort: ChildUnavailable }>;
|
|
86
|
+
readonly stopReason: "stop" | "length" | "toolUse" | "error" | "aborted";
|
|
87
|
+
/** Reasoning is a subset of output, not an additional token total. */
|
|
88
|
+
readonly tokens: Readonly<Record<"input" | "output" | "cacheRead" | "cacheWrite" | "totalTokens" | "reasoning", ChildTokenMeasurement>>;
|
|
89
|
+
}
|
|
90
|
+
export interface ResponseObservationEvent { type: typeof TASK_EVENT.RESPONSE_OBSERVATION; observation: ChildResponseObservation }
|
|
91
|
+
|
|
92
|
+
export type TaskEvent = ResponseObservationEvent | TextEvent | ThinkingEvent | ToolStartEvent | ToolUpdateEvent | ToolEndEvent | TurnEndEvent | AgentEndEvent | AgentSettledEvent | ErrorEvent | AskEvent | NoteEvent | UsageEvent;
|
|
73
93
|
|
|
74
94
|
export interface TextItem { kind: typeof THREAD_ITEM.TEXT; text: string }
|
|
75
95
|
export interface ThinkingItem { kind: typeof THREAD_ITEM.THINKING; text: string }
|
|
@@ -90,7 +110,22 @@ export interface TaskThread {
|
|
|
90
110
|
limits: ThreadLimits;
|
|
91
111
|
}
|
|
92
112
|
|
|
113
|
+
export interface RemediationTaskState extends RemediationObservations {
|
|
114
|
+
scope?: RemediationScope;
|
|
115
|
+
acquire: NativeSddAcquireRequest;
|
|
116
|
+
token?: string;
|
|
117
|
+
acquireResult?: NativeSddAttemptResult;
|
|
118
|
+
acquireUncertain?: boolean;
|
|
119
|
+
actorClaimed?: boolean;
|
|
120
|
+
settle?: NativeSddSettleRequest;
|
|
121
|
+
settlement?: NativeSddAttemptResult;
|
|
122
|
+
settlementUncertain?: boolean;
|
|
123
|
+
}
|
|
124
|
+
|
|
93
125
|
export interface TaskRecord {
|
|
126
|
+
sddRemediation?: RemediationTaskState;
|
|
127
|
+
/** Exact runtime-generated SDD preflight block retained only for continuation transport. */
|
|
128
|
+
sddPreflightContext?: string;
|
|
94
129
|
id: string;
|
|
95
130
|
agent: string;
|
|
96
131
|
mode: string;
|
|
@@ -174,9 +209,39 @@ function askRequest(raw: Raw): AskRequest {
|
|
|
174
209
|
return { id: String(raw.id ?? ""), method: String(raw.method ?? ""), title };
|
|
175
210
|
}
|
|
176
211
|
|
|
212
|
+
const CHILD_UNAVAILABLE: ChildUnavailable = Object.freeze({ state: "unavailable" });
|
|
213
|
+
const CHILD_SELECTION = Object.freeze({ provider: CHILD_UNAVAILABLE, model: CHILD_UNAVAILABLE, effort: CHILD_UNAVAILABLE });
|
|
214
|
+
|
|
215
|
+
function childMetadata(value: unknown, max: number): ChildMetadata {
|
|
216
|
+
// Retain bounded identifier fields only; do not truncate into another identity.
|
|
217
|
+
return typeof value === "string" && value.length <= max && /^[A-Za-z0-9][A-Za-z0-9._:/-]*$/.test(value)
|
|
218
|
+
? Object.freeze({ state: "observed", value }) : CHILD_UNAVAILABLE;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function childTokens(value: unknown): ChildTokenMeasurement {
|
|
222
|
+
// SDK default zeros are not provider-presence evidence (same as primary adapter).
|
|
223
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= 1_000_000_000
|
|
224
|
+
? Object.freeze({ state: "reported", value }) : CHILD_UNAVAILABLE;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function childResponse(message: Raw): ChildResponseObservation | undefined {
|
|
228
|
+
const reason = message.stopReason;
|
|
229
|
+
if (reason !== "stop" && reason !== "length" && reason !== "toolUse" && reason !== "error" && reason !== "aborted") return undefined;
|
|
230
|
+
const usage = message.usage as Raw | undefined;
|
|
231
|
+
return Object.freeze({
|
|
232
|
+
provider: childMetadata(message.provider, 32), model: childMetadata(message.model, 128),
|
|
233
|
+
responseModel: childMetadata(message.responseModel, 128), providerThinkingLevel: childMetadata(message.providerThinkingLevel, 32),
|
|
234
|
+
// RPC has no per-request selected state; get_state observes launch only.
|
|
235
|
+
selected: CHILD_SELECTION, stopReason: reason,
|
|
236
|
+
tokens: Object.freeze({ input: childTokens(usage?.input), output: childTokens(usage?.output),
|
|
237
|
+
cacheRead: childTokens(usage?.cacheRead), cacheWrite: childTokens(usage?.cacheWrite),
|
|
238
|
+
totalTokens: childTokens(usage?.totalTokens), reasoning: childTokens(usage?.reasoning) }),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
177
242
|
// One RPC line in, zero or more deltas out. Streaming deltas carry only the
|
|
178
243
|
// chunk; whole-message payloads that pi repeats on every update are ignored.
|
|
179
|
-
export function normalizeRpcEvent(raw: unknown): TaskEvent[] {
|
|
244
|
+
export function normalizeRpcEvent(raw: unknown, options: { observeResponses?: boolean } = {}): TaskEvent[] {
|
|
180
245
|
if (!raw || typeof raw !== "object") return [];
|
|
181
246
|
const event = raw as Raw;
|
|
182
247
|
switch (event.type) {
|
|
@@ -200,9 +265,16 @@ export function normalizeRpcEvent(raw: unknown): TaskEvent[] {
|
|
|
200
265
|
case "message_end": {
|
|
201
266
|
const message = event.message as Raw | undefined;
|
|
202
267
|
const usage = message?.role === "assistant" ? (message.usage as Raw | undefined) : undefined;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
268
|
+
const events: TaskEvent[] = [];
|
|
269
|
+
if (usage) {
|
|
270
|
+
const cost = usage.cost as Raw | undefined;
|
|
271
|
+
events.push({ type: TASK_EVENT.USAGE, tokens: Number(usage.totalTokens ?? 0) || 0, cost: Number(cost?.total ?? 0) || 0 });
|
|
272
|
+
}
|
|
273
|
+
if (options.observeResponses === true && message?.role === "assistant") {
|
|
274
|
+
const observation = childResponse(message);
|
|
275
|
+
if (observation) events.push({ type: TASK_EVENT.RESPONSE_OBSERVATION, observation });
|
|
276
|
+
}
|
|
277
|
+
return events;
|
|
206
278
|
}
|
|
207
279
|
case "turn_end":
|
|
208
280
|
return [{ type: TASK_EVENT.TURN_END }];
|