@automatalabs/pi-acp 0.3.2 → 0.4.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 +15 -0
- package/dist/agent.d.ts +9 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +6 -1
- package/dist/loaded-turn.d.ts +54 -0
- package/dist/loaded-turn.d.ts.map +1 -0
- package/dist/loaded-turn.js +43 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -0
- package/dist/session.d.ts +25 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +73 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -88,6 +88,21 @@ cleared synchronously before aborting so queued content never restarts generatio
|
|
|
88
88
|
cancellation. Unexpected internal failures also resolve as `{ outcome: "failed" }` rather than a
|
|
89
89
|
JSON-RPC error.
|
|
90
90
|
|
|
91
|
+
### Loaded-session turn-terminal state (`_session/loaded_turn`)
|
|
92
|
+
|
|
93
|
+
Pi also advertises the `_session/loaded_turn` extension at top-level initialize metadata as
|
|
94
|
+
`_meta: { loadedTurn: { supported: true } }` — the re-attach arm's AUTHORITATIVE completion
|
|
95
|
+
evidence for a session re-opened with `session/load` (the REPL broker's restore path).
|
|
96
|
+
`_session/loaded_turn/query { sessionId }` answers whether the loaded session's founding turn is
|
|
97
|
+
still running right now: `running` while a turn executes in this process (the client then waits
|
|
98
|
+
for the `_session/loaded_turn/ended` push — sent with the turn's stop reason, or its error, when
|
|
99
|
+
the turn finishes, arming and clearing a one-shot watch), `completed` when the session journal's
|
|
100
|
+
last message entry is an assistant message (pi persists every complete LLM message atomically at
|
|
101
|
+
`message_end`, so a completed turn always leaves an assistant leaf and the replay's trailing
|
|
102
|
+
assistant message is the turn's FINAL message — authoritative, never a quiet-gap guess), and
|
|
103
|
+
`interrupted` otherwise (the journal shows an interrupted/abandoned turn — nothing is running, so
|
|
104
|
+
re-issue is safe). A query for an unknown session is the standard `unknown_session` error.
|
|
105
|
+
|
|
91
106
|
## Development
|
|
92
107
|
|
|
93
108
|
The `pnpm test` script intentionally runs `tsc -p tsconfig.type-tests.json` before the runtime test suite. This is a small deviation from the test-script example in the frozen specification and ensures the T2b public type-contract check is enforced in local and CI test runs.
|
package/dist/agent.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AgentNotificationContext, AgentRequestContext, AuthenticateRequest, CloseSessionRequest, ForkSessionRequest, InitializeRequest, ListSessionsRequest, LoadSessionRequest, NewSessionRequest, PromptRequest, ResumeSessionRequest, SetSessionConfigOptionRequest } from "@agentclientprotocol/sdk";
|
|
2
2
|
import type { PiAcpDeps } from "./deps.js";
|
|
3
3
|
import type { SteeringRequest, SteeringResponse } from "./steering.js";
|
|
4
|
+
import type { LoadedTurnQueryRequest, LoadedTurnStatus } from "./loaded-turn.js";
|
|
4
5
|
export { PKG_VERSION } from "./version.js";
|
|
5
6
|
export declare class PiAcpAgent {
|
|
6
7
|
private readonly deps;
|
|
@@ -43,6 +44,9 @@ export declare class PiAcpAgent {
|
|
|
43
44
|
steering: {
|
|
44
45
|
supported: boolean;
|
|
45
46
|
};
|
|
47
|
+
loadedTurn: {
|
|
48
|
+
supported: boolean;
|
|
49
|
+
};
|
|
46
50
|
};
|
|
47
51
|
};
|
|
48
52
|
authenticate(context: AgentRequestContext<AuthenticateRequest>): Record<string, never>;
|
|
@@ -88,6 +92,11 @@ export declare class PiAcpAgent {
|
|
|
88
92
|
}>;
|
|
89
93
|
prompt(context: AgentRequestContext<PromptRequest>): Promise<import("@agentclientprotocol/sdk").PromptResponse>;
|
|
90
94
|
steer(context: AgentRequestContext<SteeringRequest>): Promise<SteeringResponse>;
|
|
95
|
+
/** `_session/loaded_turn/query` (see `src/loaded-turn.ts`): the loaded
|
|
96
|
+
* session's authoritative founding-turn terminal classification. */
|
|
97
|
+
loadedTurnQuery(context: AgentRequestContext<LoadedTurnQueryRequest>): {
|
|
98
|
+
status: LoadedTurnStatus;
|
|
99
|
+
};
|
|
91
100
|
cancel(context: AgentNotificationContext<{
|
|
92
101
|
sessionId: string;
|
|
93
102
|
}>): void;
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAElB,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,6BAA6B,EAC9B,MAAM,0BAA0B,CAAC;AAWlC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAS3C,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAElB,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,6BAA6B,EAC9B,MAAM,0BAA0B,CAAC;AAWlC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAS3C,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,KAAK,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGjF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AA0H3C,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAY;IACjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgC;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA8B;IACjE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA+B;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAmC;IAClE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAM;IACpC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,gBAAgB,CAAS;gBAErB,IAAI,EAAE,SAAS;IAI3B,UAAU,CAAC,QAAQ,EAAE,mBAAmB,CAAC,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAmB3D,YAAY,CAAC,OAAO,EAAE,mBAAmB,CAAC,mBAAmB,CAAC;IAI9D,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,IAAI;YAOE,UAAU;YAkCV,SAAS;IAqLvB,OAAO,CAAC,YAAY;IAKpB,UAAU,CAAC,OAAO,EAAE,mBAAmB,CAAC,iBAAiB,CAAC;;;;;IA6B1D,OAAO,CAAC,QAAQ;IAgDhB,WAAW,CAAC,OAAO,EAAE,mBAAmB,CAAC,kBAAkB,CAAC;;;;IAI5D,aAAa,CAAC,OAAO,EAAE,mBAAmB,CAAC,oBAAoB,CAAC;;;;IAIhE,WAAW,CAAC,OAAO,EAAE,mBAAmB,CAAC,kBAAkB,CAAC;;;;;IA8EtD,YAAY,CAAC,OAAO,EAAE,mBAAmB,CAAC,mBAAmB,CAAC;;;;;;;;IAqC9D,YAAY,CAAC,OAAO,EAAE,mBAAmB,CAAC,mBAAmB,CAAC;IA0BpE,OAAO,CAAC,WAAW;IAOnB,eAAe,CAAC,OAAO,EAAE,mBAAmB,CAAC,6BAA6B,CAAC;;;IAU3E,MAAM,CAAC,OAAO,EAAE,mBAAmB,CAAC,aAAa,CAAC;IAIlD,KAAK,CAAC,OAAO,EAAE,mBAAmB,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAW/E;yEACqE;IACrE,eAAe,CAAC,OAAO,EAAE,mBAAmB,CAAC,sBAAsB,CAAC,GAAG;QACrE,MAAM,EAAE,gBAAgB,CAAC;KAC1B;IAID,MAAM,CAAC,OAAO,EAAE,wBAAwB,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;YAIxD,eAAe;IAwB7B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAWV,iBAAiB;CA4BhC"}
|
package/dist/agent.js
CHANGED
|
@@ -148,7 +148,7 @@ export class PiAcpAgent {
|
|
|
148
148
|
sessionCapabilities: { resume: {}, fork: {}, list: {}, close: {} },
|
|
149
149
|
},
|
|
150
150
|
authMethods: AUTH_METHODS,
|
|
151
|
-
_meta: { steering: { supported: true } },
|
|
151
|
+
_meta: { steering: { supported: true }, loadedTurn: { supported: true } },
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
154
|
authenticate(context) {
|
|
@@ -652,6 +652,11 @@ export class PiAcpAgent {
|
|
|
652
652
|
return { outcome: "failed" };
|
|
653
653
|
});
|
|
654
654
|
}
|
|
655
|
+
/** `_session/loaded_turn/query` (see `src/loaded-turn.ts`): the loaded
|
|
656
|
+
* session's authoritative founding-turn terminal classification. */
|
|
657
|
+
loadedTurnQuery(context) {
|
|
658
|
+
return { status: this.requireLive(context.params.sessionId).loadedTurnStatus() };
|
|
659
|
+
}
|
|
655
660
|
cancel(context) {
|
|
656
661
|
this.live.get(context.params.sessionId)?.cancel();
|
|
657
662
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `_session/loaded_turn` vendor extension — turn-TERMINAL state for
|
|
3
|
+
* loaded sessions (the steering-extension precedent; the REPL broker's
|
|
4
|
+
* re-attach arm's authoritative completion evidence). Advertised at
|
|
5
|
+
* initialize via `_meta.loadedTurn.supported === true`; a client seam
|
|
6
|
+
* against a server without the advertisement degrades guest-visibly
|
|
7
|
+
* (never settles partial output, never re-issues a possibly-running
|
|
8
|
+
* turn).
|
|
9
|
+
*
|
|
10
|
+
* - `_session/loaded_turn/query` — the client asks, right after a
|
|
11
|
+
* `session/load` response, whether the loaded session's founding turn
|
|
12
|
+
* is still running RIGHT NOW. pi-acp answers honestly from its own
|
|
13
|
+
* state: `running` when a turn is executing in this process, otherwise
|
|
14
|
+
* `completed` when the session journal's last message entry is an
|
|
15
|
+
* assistant message (the founding turn's final message — pi persists
|
|
16
|
+
* each complete LLM message atomically at `message_end`, so a
|
|
17
|
+
* completed turn always leaves an assistant leaf and the replay's
|
|
18
|
+
* trailing assistant message is authoritative), else `interrupted`
|
|
19
|
+
* (the journal shows an interrupted/abandoned turn — nothing is
|
|
20
|
+
* running, so re-issue is safe).
|
|
21
|
+
* - `_session/loaded_turn/ended` — pushed when a turn that the query
|
|
22
|
+
* classified `running` ends: its ACP stop reason for a response
|
|
23
|
+
* outcome, or its error. (pi-acp can only report `running` while a
|
|
24
|
+
* turn is live in-process, and `session/load` rejects sessions that
|
|
25
|
+
* are already open in-process, so in practice the notification fires
|
|
26
|
+
* for a turn the client loaded while it was still executing — the
|
|
27
|
+
* contract is complete for that case.)
|
|
28
|
+
*/
|
|
29
|
+
export declare const LOADED_TURN_QUERY_METHOD: "_session/loaded_turn/query";
|
|
30
|
+
export declare const LOADED_TURN_ENDED_METHOD: "_session/loaded_turn/ended";
|
|
31
|
+
export type LoadedTurnStatus = "completed" | "running" | "interrupted";
|
|
32
|
+
export interface LoadedTurnQueryRequest {
|
|
33
|
+
sessionId: string;
|
|
34
|
+
}
|
|
35
|
+
export interface LoadedTurnQueryResponse {
|
|
36
|
+
status: LoadedTurnStatus;
|
|
37
|
+
}
|
|
38
|
+
export interface LoadedTurnEndedNotification {
|
|
39
|
+
sessionId: string;
|
|
40
|
+
/** The ACP stop-reason vocabulary for a turn that ended with a
|
|
41
|
+
* response; absent when the turn ended by failing. */
|
|
42
|
+
stopReason?: string;
|
|
43
|
+
/** The turn's error, when it ended by failing (the client then
|
|
44
|
+
* rejects the founding call instead of settling). */
|
|
45
|
+
error?: {
|
|
46
|
+
name: string;
|
|
47
|
+
message: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** Runtime parser used by the ACP SDK's custom-request overload. */
|
|
51
|
+
export declare const loadedTurnQueryParser: {
|
|
52
|
+
parse(value: unknown): LoadedTurnQueryRequest;
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=loaded-turn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loaded-turn.d.ts","sourceRoot":"","sources":["../src/loaded-turn.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,wBAAwB,EAAG,4BAAqC,CAAC;AAC9E,eAAO,MAAM,wBAAwB,EAAG,4BAAqC,CAAC;AAE9E,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,SAAS,GAAG,aAAa,CAAC;AAEvE,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB;2DACuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;0DACsD;IACtD,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAMD,oEAAoE;AACpE,eAAO,MAAM,qBAAqB;iBACnB,OAAO,GAAG,sBAAsB;CAM9C,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { RequestError } from "@agentclientprotocol/sdk";
|
|
2
|
+
/**
|
|
3
|
+
* The `_session/loaded_turn` vendor extension — turn-TERMINAL state for
|
|
4
|
+
* loaded sessions (the steering-extension precedent; the REPL broker's
|
|
5
|
+
* re-attach arm's authoritative completion evidence). Advertised at
|
|
6
|
+
* initialize via `_meta.loadedTurn.supported === true`; a client seam
|
|
7
|
+
* against a server without the advertisement degrades guest-visibly
|
|
8
|
+
* (never settles partial output, never re-issues a possibly-running
|
|
9
|
+
* turn).
|
|
10
|
+
*
|
|
11
|
+
* - `_session/loaded_turn/query` — the client asks, right after a
|
|
12
|
+
* `session/load` response, whether the loaded session's founding turn
|
|
13
|
+
* is still running RIGHT NOW. pi-acp answers honestly from its own
|
|
14
|
+
* state: `running` when a turn is executing in this process, otherwise
|
|
15
|
+
* `completed` when the session journal's last message entry is an
|
|
16
|
+
* assistant message (the founding turn's final message — pi persists
|
|
17
|
+
* each complete LLM message atomically at `message_end`, so a
|
|
18
|
+
* completed turn always leaves an assistant leaf and the replay's
|
|
19
|
+
* trailing assistant message is authoritative), else `interrupted`
|
|
20
|
+
* (the journal shows an interrupted/abandoned turn — nothing is
|
|
21
|
+
* running, so re-issue is safe).
|
|
22
|
+
* - `_session/loaded_turn/ended` — pushed when a turn that the query
|
|
23
|
+
* classified `running` ends: its ACP stop reason for a response
|
|
24
|
+
* outcome, or its error. (pi-acp can only report `running` while a
|
|
25
|
+
* turn is live in-process, and `session/load` rejects sessions that
|
|
26
|
+
* are already open in-process, so in practice the notification fires
|
|
27
|
+
* for a turn the client loaded while it was still executing — the
|
|
28
|
+
* contract is complete for that case.)
|
|
29
|
+
*/
|
|
30
|
+
export const LOADED_TURN_QUERY_METHOD = "_session/loaded_turn/query";
|
|
31
|
+
export const LOADED_TURN_ENDED_METHOD = "_session/loaded_turn/ended";
|
|
32
|
+
function isRecord(value) {
|
|
33
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34
|
+
}
|
|
35
|
+
/** Runtime parser used by the ACP SDK's custom-request overload. */
|
|
36
|
+
export const loadedTurnQueryParser = {
|
|
37
|
+
parse(value) {
|
|
38
|
+
if (!isRecord(value) || typeof value.sessionId !== "string") {
|
|
39
|
+
throw RequestError.invalidParams(undefined, "invalid _session/loaded_turn/query request");
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
},
|
|
43
|
+
};
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,MAAM,EACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,MAAM,EACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAIxD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,MAAM,CAAC,OAAO,GAAE,aAAkB;;;GAsBvD"}
|
package/dist/server.js
CHANGED
|
@@ -3,6 +3,7 @@ import { agent as acpAgent, methods, ndJsonStream, } from "@agentclientprotocol/
|
|
|
3
3
|
import { PiAcpAgent } from "./agent.js";
|
|
4
4
|
import { resolveDeps } from "./deps.js";
|
|
5
5
|
import { SESSION_STEERING_METHOD, steeringRequestParser } from "./steering.js";
|
|
6
|
+
import { LOADED_TURN_QUERY_METHOD, loadedTurnQueryParser } from "./loaded-turn.js";
|
|
6
7
|
export { PiAcpAgent } from "./agent.js";
|
|
7
8
|
export async function runAcp(options = {}) {
|
|
8
9
|
const impl = new PiAcpAgent(await resolveDeps(options.deps));
|
|
@@ -18,6 +19,7 @@ export async function runAcp(options = {}) {
|
|
|
18
19
|
.onRequest(methods.agent.session.setConfigOption, (context) => impl.setConfigOption(context))
|
|
19
20
|
.onRequest(methods.agent.session.prompt, (context) => impl.prompt(context))
|
|
20
21
|
.onRequest(SESSION_STEERING_METHOD, steeringRequestParser, (context) => impl.steer(context))
|
|
22
|
+
.onRequest(LOADED_TURN_QUERY_METHOD, loadedTurnQueryParser, (context) => impl.loadedTurnQuery(context))
|
|
21
23
|
.onNotification(methods.agent.session.cancel, (context) => impl.cancel(context));
|
|
22
24
|
const stream = options.stream ?? ndJsonStream(Writable.toWeb(process.stdout), Readable.toWeb(process.stdin));
|
|
23
25
|
const connection = app.connect(stream);
|
package/dist/session.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { PiAcpDeps } from "./deps.js";
|
|
|
5
5
|
import type { McpBridge, McpResultProjection } from "./mcp-bridge.js";
|
|
6
6
|
import { type ChildProcessRegistrySlot } from "./child-process-registry.js";
|
|
7
7
|
import type { SteeringRequest, SteeringResponse } from "./steering.js";
|
|
8
|
+
import { type LoadedTurnStatus } from "./loaded-turn.js";
|
|
8
9
|
export interface PiSessionOptions {
|
|
9
10
|
sessionId: string;
|
|
10
11
|
session: AgentSession;
|
|
@@ -45,8 +46,32 @@ export declare class PiSession {
|
|
|
45
46
|
private cleanupGeneration;
|
|
46
47
|
private resourceDisposePromise;
|
|
47
48
|
private bridgeClosePromise;
|
|
49
|
+
/** The `_session/loaded_turn` extension's watch flag: set when a
|
|
50
|
+
* `loadedTurnStatus()` query answered `running` (a client is waiting
|
|
51
|
+
* for that turn's authoritative end), cleared — and the
|
|
52
|
+
* `_session/loaded_turn/ended` notification sent — when the turn
|
|
53
|
+
* finishes for any reason (response outcome with its stop reason, or
|
|
54
|
+
* a failure with its error). */
|
|
55
|
+
private loadedTurnReportedRunning;
|
|
48
56
|
constructor(options: PiSessionOptions);
|
|
49
57
|
get busy(): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* The `_session/loaded_turn/query` answer — the authoritative
|
|
60
|
+
* founding-turn terminal classification for a loaded session (see
|
|
61
|
+
* `packages/pi-acp/src/loaded-turn.ts`):
|
|
62
|
+
*
|
|
63
|
+
* - `running` when a turn is executing in this process right now (the
|
|
64
|
+
* client then waits for the `_session/loaded_turn/ended` push — the
|
|
65
|
+
* watch flag is armed here so the turn's finish sends it),
|
|
66
|
+
* - `completed` when the session journal's last message entry is an
|
|
67
|
+
* assistant message (pi persists every complete LLM message
|
|
68
|
+
* atomically at `message_end`, so a completed turn always leaves an
|
|
69
|
+
* assistant leaf and the replay's trailing assistant message is the
|
|
70
|
+
* turn's FINAL message — authoritative, never a quiet-gap guess),
|
|
71
|
+
* - `interrupted` otherwise (the journal shows an interrupted or
|
|
72
|
+
* abandoned turn — no turn is running, so re-issue is safe).
|
|
73
|
+
*/
|
|
74
|
+
loadedTurnStatus(): LoadedTurnStatus;
|
|
50
75
|
configOptions(): import("@agentclientprotocol/sdk").SessionConfigOption[];
|
|
51
76
|
publishAvailableModels(models: readonly Model<Api>[]): void;
|
|
52
77
|
activeTurnSignal(): AbortSignal | undefined;
|
package/dist/session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAClG,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAc3C,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAuB,KAAK,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACjG,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/dist/session.js
CHANGED
|
@@ -9,6 +9,7 @@ import { translateEvent } from "./translate.js";
|
|
|
9
9
|
import { agentMessages, promptUsage, terminalAssistant, usageUpdate, } from "./usage.js";
|
|
10
10
|
import { installPermissionWrapper } from "./permissions.js";
|
|
11
11
|
import { ChildCleanupFailure } from "./child-process-registry.js";
|
|
12
|
+
import { LOADED_TURN_ENDED_METHOD, } from "./loaded-turn.js";
|
|
12
13
|
export class PiSession {
|
|
13
14
|
sessionId;
|
|
14
15
|
pi;
|
|
@@ -36,6 +37,13 @@ export class PiSession {
|
|
|
36
37
|
cleanupGeneration;
|
|
37
38
|
resourceDisposePromise;
|
|
38
39
|
bridgeClosePromise;
|
|
40
|
+
/** The `_session/loaded_turn` extension's watch flag: set when a
|
|
41
|
+
* `loadedTurnStatus()` query answered `running` (a client is waiting
|
|
42
|
+
* for that turn's authoritative end), cleared — and the
|
|
43
|
+
* `_session/loaded_turn/ended` notification sent — when the turn
|
|
44
|
+
* finishes for any reason (response outcome with its stop reason, or
|
|
45
|
+
* a failure with its error). */
|
|
46
|
+
loadedTurnReportedRunning = false;
|
|
39
47
|
constructor(options) {
|
|
40
48
|
this.sessionId = options.sessionId;
|
|
41
49
|
this.pi = options.session;
|
|
@@ -80,6 +88,36 @@ export class PiSession {
|
|
|
80
88
|
get busy() {
|
|
81
89
|
return this.activeTurn !== undefined || this.configReserved || this.closing;
|
|
82
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* The `_session/loaded_turn/query` answer — the authoritative
|
|
93
|
+
* founding-turn terminal classification for a loaded session (see
|
|
94
|
+
* `packages/pi-acp/src/loaded-turn.ts`):
|
|
95
|
+
*
|
|
96
|
+
* - `running` when a turn is executing in this process right now (the
|
|
97
|
+
* client then waits for the `_session/loaded_turn/ended` push — the
|
|
98
|
+
* watch flag is armed here so the turn's finish sends it),
|
|
99
|
+
* - `completed` when the session journal's last message entry is an
|
|
100
|
+
* assistant message (pi persists every complete LLM message
|
|
101
|
+
* atomically at `message_end`, so a completed turn always leaves an
|
|
102
|
+
* assistant leaf and the replay's trailing assistant message is the
|
|
103
|
+
* turn's FINAL message — authoritative, never a quiet-gap guess),
|
|
104
|
+
* - `interrupted` otherwise (the journal shows an interrupted or
|
|
105
|
+
* abandoned turn — no turn is running, so re-issue is safe).
|
|
106
|
+
*/
|
|
107
|
+
loadedTurnStatus() {
|
|
108
|
+
const turn = this.activeTurn;
|
|
109
|
+
if (turn !== undefined && !turn.completed && !this.closing) {
|
|
110
|
+
this.loadedTurnReportedRunning = true;
|
|
111
|
+
return "running";
|
|
112
|
+
}
|
|
113
|
+
const leaf = this.manager.getLeafEntry();
|
|
114
|
+
if (leaf !== undefined &&
|
|
115
|
+
leaf.type === "message" &&
|
|
116
|
+
leaf.message.role === "assistant") {
|
|
117
|
+
return "completed";
|
|
118
|
+
}
|
|
119
|
+
return "interrupted";
|
|
120
|
+
}
|
|
83
121
|
configOptions() {
|
|
84
122
|
return [thinkingLevelOption(this.pi), modelOption(this.pi, this.availableModels)];
|
|
85
123
|
}
|
|
@@ -184,6 +222,26 @@ export class PiSession {
|
|
|
184
222
|
turn.releaseBoundary = undefined;
|
|
185
223
|
if (this.activeTurn === turn)
|
|
186
224
|
this.activeTurn = undefined;
|
|
225
|
+
// The `_session/loaded_turn` extension's authoritative end push: a
|
|
226
|
+
// client that was told this turn was `running` at query time gets the
|
|
227
|
+
// ended notification now — with the turn's stop reason (response
|
|
228
|
+
// outcome) or its error (failure), never both. Best-effort: a
|
|
229
|
+
// failing notification must not break the turn's settlement. The
|
|
230
|
+
// push is ORDERED behind the turn's final update pump (review round
|
|
231
|
+
// 6): the last deltas were only enqueued (the pump delivers them
|
|
232
|
+
// asynchronously), and the re-attach seam settles with the
|
|
233
|
+
// accumulated text at the terminal marker — a marker delivered
|
|
234
|
+
// before the final chunk would durably settle PARTIAL text.
|
|
235
|
+
if (this.loadedTurnReportedRunning) {
|
|
236
|
+
this.loadedTurnReportedRunning = false;
|
|
237
|
+
const notification = "response" in outcome
|
|
238
|
+
? { sessionId: this.sessionId, stopReason: outcome.response.stopReason }
|
|
239
|
+
: { sessionId: this.sessionId, error: normalizeTurnError(outcome.error) };
|
|
240
|
+
void this.drain()
|
|
241
|
+
.catch(() => undefined)
|
|
242
|
+
.then(() => this.client.notify(LOADED_TURN_ENDED_METHOD, notification))
|
|
243
|
+
.catch(() => undefined);
|
|
244
|
+
}
|
|
187
245
|
const generation = this.cleanupGeneration;
|
|
188
246
|
if (generation?.resumeRefreshesOnSettlement && generation.mode === "cancel-only") {
|
|
189
247
|
this.cleanupGeneration = undefined;
|
|
@@ -743,3 +801,18 @@ export class PiSession {
|
|
|
743
801
|
});
|
|
744
802
|
}
|
|
745
803
|
}
|
|
804
|
+
/** Normalize a turn's failure into the loaded-turn ended notification's
|
|
805
|
+
* `{ name, message }` error shape (best-effort — the notification
|
|
806
|
+
* carries the turn's error verbatim-ish, never the whole stack). */
|
|
807
|
+
function normalizeTurnError(error) {
|
|
808
|
+
if (error !== null && typeof error === "object") {
|
|
809
|
+
const candidate = error;
|
|
810
|
+
return {
|
|
811
|
+
name: typeof candidate.name === "string" ? candidate.name : "Error",
|
|
812
|
+
message: typeof candidate.message === "string"
|
|
813
|
+
? candidate.message
|
|
814
|
+
: String(candidate.message ?? error),
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
return { name: "Error", message: String(error) };
|
|
818
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automatalabs/pi-acp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.19.0"
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@agentclientprotocol/sdk": "1.3.0",
|
|
34
|
-
"@earendil-works/pi-ai": "0.84.
|
|
35
|
-
"@earendil-works/pi-coding-agent": "0.84.
|
|
34
|
+
"@earendil-works/pi-ai": "0.84.1",
|
|
35
|
+
"@earendil-works/pi-coding-agent": "0.84.1",
|
|
36
36
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
37
37
|
"typebox": "1.3.2"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@earendil-works/pi-agent-core": "0.84.
|
|
40
|
+
"@earendil-works/pi-agent-core": "0.84.1"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "tsc -b",
|