@code-yeongyu/senpi 2026.10.25-2 → 2026.10.25-3
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/CHANGELOG.md +20 -0
- package/dist/bundle/chunks/{args-JXULI4EH.js → args-B444OQBK.js} +1 -1
- package/dist/bundle/chunks/{auth-4NUNUSTF.js → auth-C77UQA55.js} +1 -1
- package/dist/bundle/chunks/chunk-6O4SEW2W.js +5 -0
- package/dist/bundle/chunks/{chunk-Y5OTHMXO.js → chunk-7CNOCJCN.js} +1 -1
- package/dist/bundle/chunks/{chunk-JRXKLIEQ.js → chunk-7W7OJDIY.js} +4 -4
- package/dist/bundle/chunks/chunk-AX72VNRS.js +27 -0
- package/dist/bundle/chunks/{chunk-LYGAV6KU.js → chunk-C5UFJ7B3.js} +1 -1
- package/dist/bundle/chunks/{chunk-G2UETS2R.js → chunk-CNL6QSUB.js} +4 -4
- package/dist/bundle/chunks/{chunk-2EFVQWXZ.js → chunk-FFQOEC76.js} +3 -3
- package/dist/bundle/chunks/chunk-H3HNMY2W.js +13 -0
- package/dist/bundle/chunks/{chunk-W2BKZBYJ.js → chunk-HVJI5T75.js} +1 -1
- package/dist/bundle/chunks/{chunk-YN7ELQ43.js → chunk-JA2R2ZSE.js} +1 -1
- package/dist/bundle/chunks/chunk-JPGT64RV.js +2 -0
- package/dist/bundle/chunks/chunk-KXB5WIS5.js +4 -0
- package/dist/bundle/chunks/chunk-L73UYAH5.js +3 -0
- package/dist/bundle/chunks/chunk-LTU6UFEX.js +2 -0
- package/dist/bundle/chunks/{chunk-KUE4AQN6.js → chunk-MJ5NNWX4.js} +1 -1
- package/dist/bundle/chunks/{chunk-WFXUCVIR.js → chunk-O3Q2ET6W.js} +2 -5
- package/dist/bundle/chunks/{chunk-MXZPAUFB.js → chunk-ODTGC6YU.js} +1 -1
- package/dist/bundle/chunks/chunk-P3M2PF6K.js +27 -0
- package/dist/bundle/chunks/{chunk-VNN2V74Y.js → chunk-SWJXRC2I.js} +1 -1
- package/dist/bundle/chunks/{chunk-RXKNOEQQ.js → chunk-UQNRW3VU.js} +1 -1
- package/dist/bundle/chunks/{chunk-U67ML6CQ.js → chunk-WD6WBYC5.js} +1 -1
- package/dist/bundle/chunks/chunk-WJOT3DFL.js +2 -0
- package/dist/bundle/chunks/{chunk-XBUSVKWC.js → chunk-WLHNEUGD.js} +1 -1
- package/dist/bundle/chunks/chunk-YI7SPFDN.js +5 -0
- package/dist/bundle/chunks/{chunk-J2U5VDG7.js → chunk-YRF4I43T.js} +28 -28
- package/dist/bundle/chunks/chunk-Z3UYNSVR.js +3 -0
- package/dist/bundle/chunks/cli-main-HEKFFB5S.js +3 -0
- package/dist/bundle/chunks/{client-KWTLTKVV.js → client-WTYEICLH.js} +3 -3
- package/dist/bundle/chunks/dispatch-NYAULNVW.js +9 -0
- package/dist/bundle/chunks/{help-fast-path-ZHZGXAUG.js → help-fast-path-3Z26DQSV.js} +1 -1
- package/dist/bundle/chunks/host-command-Z3WPHJOW.js +2 -0
- package/dist/bundle/chunks/host-lifecycle-ONQIMWMX.js +3 -0
- package/dist/bundle/chunks/host-lifecycle.js +7 -5
- package/dist/bundle/chunks/host-session-command-J65ENSV5.js +20 -0
- package/dist/bundle/chunks/{host-socket-request-AS2OCRLP.js → host-socket-request-3CWE4265.js} +1 -1
- package/dist/bundle/chunks/interactive-mode-DEPQC6TX.js +2 -0
- package/dist/bundle/chunks/mcp-presenter-bootstrap-XUSCQVRC.js +2 -0
- package/dist/bundle/chunks/{multi-session-host-VTTY5MFT.js → multi-session-host-YI2WZOET.js} +12 -9
- package/dist/bundle/chunks/{package-manager-cli-DZFOC3MF.js → package-manager-cli-GHZPJMRF.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-U7YQMC7H.js → rotation-stream-ZPVCFOVD.js} +1 -1
- package/dist/bundle/chunks/rpc-mode-ORHBGBBV.js +2 -0
- package/dist/bundle/chunks/{rpc-monitor-source-B5NQ4ZVH.js → rpc-monitor-source-FFEQS744.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-SLISTG4V.js → schedule-command-YWG6D6ZF.js} +1 -1
- package/dist/bundle/chunks/session-control-endpoint-36HAQSQO.js +3 -0
- package/dist/bundle/chunks/{session-picker-465FVXQG.js → session-picker-25H5KNYP.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +278 -264
- package/dist/bundle/chunks/{stdio-IEC635SY.js → stdio-YXDKKKQZ.js} +1 -1
- package/dist/bundle/chunks/{streamableHttp-KJI5KE4K.js → streamableHttp-DKIBUWVY.js} +1 -1
- package/dist/bundle/chunks/{types-LLNV23LW.js → types-BJUEQ4II.js} +1 -1
- package/dist/bundle/chunks/zod-compat-RJDXCU7S.js +2 -0
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/host-supervisor.js +7 -5
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.js +9 -0
- package/dist/cli/host-command-args.d.ts +1 -0
- package/dist/cli/host-command-args.js +7 -1
- package/dist/cli/host-command.js +3 -1
- package/dist/core/agent-session.js +22 -7
- package/dist/core/extensions/builtin/goal/index.js +1 -0
- package/dist/core/extensions/builtin/goal/lifecycle-facts.d.ts +3 -0
- package/dist/core/extensions/builtin/mcp/catalog.d.ts +3 -1
- package/dist/core/extensions/builtin/mcp/catalog.js +2 -0
- package/dist/core/extensions/builtin/mcp/connection.d.ts +1 -0
- package/dist/core/extensions/builtin/mcp/connection.js +46 -1
- package/dist/core/extensions/builtin/mcp/elicitation.d.ts +6 -1
- package/dist/core/extensions/builtin/mcp/elicitation.js +56 -2
- package/dist/core/extensions/builtin/mcp/expose/call.js +6 -2
- package/dist/core/extensions/builtin/mcp/expose/session.d.ts +4 -1
- package/dist/core/extensions/builtin/mcp/expose/session.js +7 -0
- package/dist/core/extensions/builtin/mcp/health.js +5 -0
- package/dist/core/extensions/builtin/mcp/host-registry.js +7 -1
- package/dist/core/extensions/builtin/mcp/index.js +2 -0
- package/dist/core/extensions/builtin/mcp/modern/client.d.ts +5 -0
- package/dist/core/extensions/builtin/mcp/modern/client.js +15 -0
- package/dist/core/extensions/builtin/mcp/modern/consent-queue.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/modern/consent-queue.js +59 -0
- package/dist/core/extensions/builtin/mcp/modern/diagnostics.d.ts +4 -0
- package/dist/core/extensions/builtin/mcp/modern/diagnostics.js +39 -0
- package/dist/core/extensions/builtin/mcp/modern/dispatch.js +21 -11
- package/dist/core/extensions/builtin/mcp/modern/elicitation.d.ts +14 -0
- package/dist/core/extensions/builtin/mcp/modern/elicitation.js +145 -0
- package/dist/core/extensions/builtin/mcp/modern/errors.d.ts +21 -0
- package/dist/core/extensions/builtin/mcp/modern/errors.js +23 -0
- package/dist/core/extensions/builtin/mcp/modern/form-schema.d.ts +20 -0
- package/dist/core/extensions/builtin/mcp/modern/form-schema.js +153 -0
- package/dist/core/extensions/builtin/mcp/modern/http-fetch.d.ts +3 -1
- package/dist/core/extensions/builtin/mcp/modern/http-fetch.js +8 -2
- package/dist/core/extensions/builtin/mcp/modern/inputs.d.ts +11 -0
- package/dist/core/extensions/builtin/mcp/modern/inputs.js +65 -0
- package/dist/core/extensions/builtin/mcp/modern/parameter-headers.d.ts +11 -0
- package/dist/core/extensions/builtin/mcp/modern/parameter-headers.js +109 -0
- package/dist/core/extensions/builtin/mcp/modern/request.d.ts +20 -0
- package/dist/core/extensions/builtin/mcp/modern/request.js +292 -0
- package/dist/core/extensions/builtin/mcp/modern/stdio-probe.d.ts +1 -1
- package/dist/core/extensions/builtin/mcp/modern/stdio-probe.js +54 -6
- package/dist/core/extensions/builtin/mcp/modern/transport.js +80 -14
- package/dist/core/extensions/builtin/mcp/modern/wire.js +11 -7
- package/dist/core/extensions/builtin/mcp/prompts.d.ts +5 -1
- package/dist/core/extensions/builtin/mcp/prompts.js +6 -1
- package/dist/core/extensions/builtin/mcp/request-origin.d.ts +47 -0
- package/dist/core/extensions/builtin/mcp/request-origin.js +59 -0
- package/dist/core/extensions/builtin/mcp/resources.d.ts +4 -1
- package/dist/core/extensions/builtin/mcp/resources.js +19 -4
- package/dist/core/extensions/builtin/mcp/sdk.lazy.d.ts +3 -1
- package/dist/core/extensions/builtin/mcp/sdk.lazy.js +3 -1
- package/dist/core/extensions/builtin/mcp/service-register.d.ts +4 -1
- package/dist/core/extensions/builtin/mcp/service-register.js +3 -0
- package/dist/core/extensions/builtin/mcp/service-types.d.ts +2 -1
- package/dist/core/extensions/builtin/mcp/service.d.ts +1 -0
- package/dist/core/extensions/builtin/mcp/service.js +79 -4
- package/dist/core/extensions/builtin/mcp/shared-connection.js +36 -2
- package/dist/core/extensions/builtin/mcp/shared-lease.d.ts +1 -0
- package/dist/core/extensions/builtin/mcp/shared-lease.js +9 -0
- package/dist/core/extensions/builtin/mcp/transport-sdk.d.ts +3 -2
- package/dist/core/extensions/builtin/mcp/transport-sdk.js +56 -25
- package/dist/core/extensions/builtin/mcp/wrap.d.ts +7 -0
- package/dist/core/extensions/builtin/mcp/wrap.js +6 -0
- package/dist/core/extensions/runner.js +3 -2
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/lifecycle-ledger.d.ts +1 -0
- package/dist/core/lifecycle-ledger.js +7 -2
- package/dist/core/lifecycle-record.js +3 -0
- package/dist/core/lifecycle-session-binding.d.ts +11 -1
- package/dist/core/lifecycle-session-binding.js +29 -2
- package/dist/core/lifecycle-stop.d.ts +41 -0
- package/dist/core/lifecycle-stop.js +92 -0
- package/dist/core/mcp-form-types.d.ts +89 -0
- package/dist/core/mcp-form-types.js +23 -0
- package/dist/core/mcp-mrtr-presentations.d.ts +15 -0
- package/dist/core/mcp-mrtr-presentations.js +17 -0
- package/dist/core/session-lifecycle-types.d.ts +15 -0
- package/dist/core/session-lifecycle.d.ts +17 -1
- package/dist/core/session-lifecycle.js +40 -2
- package/dist/main.js +5 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +1 -0
- package/dist/modes/interactive/components/extension-selector.js +4 -0
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.js +34 -8
- package/dist/modes/interactive/mcp-local-presenter.d.ts +5 -0
- package/dist/modes/interactive/mcp-local-presenter.js +109 -0
- package/dist/modes/rpc/connection-handler.d.ts +1 -0
- package/dist/modes/rpc/connection-handler.js +7 -1
- package/dist/modes/rpc/host-capabilities.d.ts +1 -0
- package/dist/modes/rpc/host-capabilities.js +2 -1
- package/dist/modes/rpc/host-ensure-start.js +13 -2
- package/dist/modes/rpc/host-ensure-types.d.ts +2 -0
- package/dist/modes/rpc/host-ensure.js +1 -0
- package/dist/modes/rpc/host-handoff.d.ts +2 -0
- package/dist/modes/rpc/host-handover-wire.d.ts +2 -0
- package/dist/modes/rpc/host-handover-wire.js +1 -0
- package/dist/modes/rpc/host-lifecycle-launch.d.ts +1 -0
- package/dist/modes/rpc/host-lifecycle-launch.js +43 -2
- package/dist/modes/rpc/host-runner.d.ts +2 -0
- package/dist/modes/rpc/host-runner.js +8 -6
- package/dist/modes/rpc/host-successor.js +11 -1
- package/dist/modes/rpc/mcp-form-dialog.d.ts +45 -0
- package/dist/modes/rpc/mcp-form-dialog.js +159 -0
- package/dist/modes/rpc/mcp-form-worker.d.ts +14 -0
- package/dist/modes/rpc/mcp-form-worker.js +167 -0
- package/dist/modes/rpc/mcp-presenter-authority.d.ts +53 -0
- package/dist/modes/rpc/mcp-presenter-authority.js +309 -0
- package/dist/modes/rpc/mcp-presenter-bootstrap.d.ts +8 -0
- package/dist/modes/rpc/mcp-presenter-bootstrap.js +111 -0
- package/dist/modes/rpc/mcp-presenter-control.d.ts +24 -0
- package/dist/modes/rpc/mcp-presenter-control.js +330 -0
- package/dist/modes/rpc/multi-session-host.d.ts +3 -0
- package/dist/modes/rpc/multi-session-host.js +36 -1
- package/dist/modes/rpc/session-binding.js +6 -1
- package/dist/modes/rpc/session-event-writer.d.ts +14 -0
- package/dist/modes/rpc/session-event-writer.js +128 -1
- package/dist/modes/rpc/session-router-routing.js +1 -0
- package/dist/modes/rpc/session-worker-client.d.ts +3 -0
- package/dist/modes/rpc/session-worker-client.js +89 -0
- package/dist/modes/rpc/session-worker-protocol.d.ts +41 -0
- package/dist/modes/rpc/session-worker.js +9 -0
- package/docs/extensions.md +5 -0
- package/docs/mcp.md +20 -3
- package/docs/rpc.md +111 -0
- package/package.json +6 -6
- package/dist/bundle/chunks/chunk-B6TPQDBW.js +0 -5
- package/dist/bundle/chunks/chunk-CDRFFHLL.js +0 -4
- package/dist/bundle/chunks/chunk-HPCS4HM4.js +0 -27
- package/dist/bundle/chunks/chunk-KJZAY5YH.js +0 -27
- package/dist/bundle/chunks/chunk-OI4NMCKP.js +0 -3
- package/dist/bundle/chunks/chunk-SCVRKIFK.js +0 -2
- package/dist/bundle/chunks/cli-main-SWKFMTZQ.js +0 -3
- package/dist/bundle/chunks/dispatch-DEP4PEDZ.js +0 -10
- package/dist/bundle/chunks/host-command-BC6UWP33.js +0 -2
- package/dist/bundle/chunks/host-lifecycle-TN5NEGNF.js +0 -3
- package/dist/bundle/chunks/host-session-command-MGVBLBRV.js +0 -20
- package/dist/bundle/chunks/interactive-mode-SCSROHD5.js +0 -2
- package/dist/bundle/chunks/rpc-mode-TOXYD4XA.js +0 -2
- package/dist/bundle/chunks/session-control-endpoint-HFFMBALG.js +0 -3
|
@@ -8,11 +8,14 @@ import { readLifecycleGoal } from "./lifecycle-goal.js";
|
|
|
8
8
|
import { lifecycleHistory } from "./lifecycle-history.js";
|
|
9
9
|
import { bindLifecycleHumanInput } from "./lifecycle-human-input.js";
|
|
10
10
|
import { readLifecycleRuntimeFacts } from "./lifecycle-runtime-facts.js";
|
|
11
|
+
import { goalGuardAfter } from "./lifecycle-stop.js";
|
|
11
12
|
import { SessionLifecycleController } from "./session-lifecycle.js";
|
|
12
13
|
import { isRecord } from "./work-custody-record.js";
|
|
13
14
|
/** Attaches durable lifecycle to the real session event/admission boundaries. */
|
|
14
15
|
export class LifecycleSessionBinding {
|
|
15
16
|
constructor(session, host) {
|
|
17
|
+
/** The run each emitted agent_end belonged to, read when it was emitted, not when its queue reaches it. */
|
|
18
|
+
this.boundEnds = new WeakMap();
|
|
16
19
|
this.refreshGeneration = 0;
|
|
17
20
|
this.restored = false;
|
|
18
21
|
/** After dispose() nothing the controller decides is written or emitted: the session file is no longer this session's. */
|
|
@@ -102,10 +105,20 @@ export class LifecycleSessionBinding {
|
|
|
102
105
|
}
|
|
103
106
|
startRun() {
|
|
104
107
|
this.lastEnd = undefined;
|
|
108
|
+
this.lastEndGuard = undefined;
|
|
105
109
|
const origin = this.nextRunOrigin;
|
|
106
110
|
this.nextRunOrigin = undefined;
|
|
107
111
|
this.controller.startRun(origin);
|
|
108
112
|
}
|
|
113
|
+
deadlineStopped(kind, atMs) {
|
|
114
|
+
this.controller.deadlineStopped(kind, atMs);
|
|
115
|
+
}
|
|
116
|
+
deadlineStopWriteFailed() {
|
|
117
|
+
this.controller.deadlineStopWriteFailed();
|
|
118
|
+
}
|
|
119
|
+
bindTurnEnd(end) {
|
|
120
|
+
this.boundEnds.set(end, this.controller.ledger.runId);
|
|
121
|
+
}
|
|
109
122
|
consume(message) {
|
|
110
123
|
return this.write(() => consumeLifecycleDelivery(this.session, this.controller, message));
|
|
111
124
|
}
|
|
@@ -115,8 +128,14 @@ export class LifecycleSessionBinding {
|
|
|
115
128
|
const ticketId = messageTicket(message);
|
|
116
129
|
return ticketId ? this.session.sessionManager.reduceHistory(lifecycleDeliveryEntries).get(ticketId) : undefined;
|
|
117
130
|
}
|
|
118
|
-
|
|
131
|
+
/** `event` is the extension agent_end, never the session-level stamp; `end` is the core event bound at emission. */
|
|
132
|
+
async turnEnded(event, end, guardStartLeafId) {
|
|
119
133
|
this.lastEnd = event;
|
|
134
|
+
this.lastEndRunId = this.boundEnds.get(end);
|
|
135
|
+
this.lastEndGuard =
|
|
136
|
+
guardStartLeafId === undefined
|
|
137
|
+
? undefined
|
|
138
|
+
: goalGuardAfter(this.session.sessionManager.getBranch(), guardStartLeafId);
|
|
120
139
|
await this.refreshFacts();
|
|
121
140
|
}
|
|
122
141
|
async refreshFacts(changedGoal) {
|
|
@@ -166,7 +185,15 @@ export class LifecycleSessionBinding {
|
|
|
166
185
|
deadlineStop: input.deadlineStop || saved.deadlineStop,
|
|
167
186
|
providerRetryAtMs: input.providerRetryAtMs ?? saved.providerRetryAtMs,
|
|
168
187
|
};
|
|
169
|
-
this
|
|
188
|
+
// Stop facts belong to the run that just ended in this process; a restored end carries its recorded stop.
|
|
189
|
+
const stop = this.lastEnd
|
|
190
|
+
? {
|
|
191
|
+
runId: this.lastEndRunId,
|
|
192
|
+
guard: this.lastEndGuard,
|
|
193
|
+
todo: collected?.todoOutcome,
|
|
194
|
+
}
|
|
195
|
+
: undefined;
|
|
196
|
+
this.controller.goalFacts(goal, input, stop);
|
|
170
197
|
}
|
|
171
198
|
async idle() {
|
|
172
199
|
await this.refreshFacts();
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { TodoOwedOutcome } from "./extensions/builtin/goal/todo-owed-backstop.ts";
|
|
2
|
+
import type { TurnEndLivenessInput } from "./extensions/builtin/goal/turn-end-liveness.ts";
|
|
3
|
+
import type { LifecycleStop } from "./session-lifecycle-types.ts";
|
|
4
|
+
import type { TurnDeadlineKind } from "./turn-deadline.ts";
|
|
5
|
+
/** Stop facts the stopping run produced itself, read from that run's own end (senpi#3864). */
|
|
6
|
+
export interface RunStopFacts {
|
|
7
|
+
/** The run the turn end belonged to when its agent_end was emitted. */
|
|
8
|
+
readonly runId?: string;
|
|
9
|
+
readonly guard?: {
|
|
10
|
+
readonly reason: string;
|
|
11
|
+
readonly at: number;
|
|
12
|
+
};
|
|
13
|
+
readonly todo?: TodoOwedOutcome;
|
|
14
|
+
}
|
|
15
|
+
/** A deadline captured when it fired, before its durable write, for the run it stopped. */
|
|
16
|
+
export interface DeadlineStopCapture {
|
|
17
|
+
readonly runId: string;
|
|
18
|
+
readonly kind: TurnDeadlineKind;
|
|
19
|
+
readonly at: string;
|
|
20
|
+
readonly persisted?: false;
|
|
21
|
+
}
|
|
22
|
+
type StopDetail = Omit<LifecycleStop, "runId" | "epoch" | "at"> & {
|
|
23
|
+
readonly at?: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* First match wins: the run's own deadline explains its system abort, a guard explains a refused continuation, then
|
|
27
|
+
* the classified abort, and `no_wake` only for a stall no hold or guard owns. Provider failures keep the failed path.
|
|
28
|
+
*/
|
|
29
|
+
export declare function stopDetail(input: TurnEndLivenessInput | undefined, facts: RunStopFacts | undefined, deadline: DeadlineStopCapture | undefined, stalled: boolean): StopDetail | undefined;
|
|
30
|
+
export declare function sameStop(stop: LifecycleStop, detail: StopDetail): boolean;
|
|
31
|
+
/** The latest guard appended within one bound end's extension-processing window, including legacy entries. */
|
|
32
|
+
export declare function goalGuardAfter(branch: readonly {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly type: string;
|
|
35
|
+
readonly customType?: string;
|
|
36
|
+
readonly data?: unknown;
|
|
37
|
+
}[], guardStartLeafId: string | null): RunStopFacts["guard"];
|
|
38
|
+
/** Malformed optional detail is dropped; it never invalidates the record that carries it. */
|
|
39
|
+
export declare function readLastStop(value: unknown, runId: unknown): LifecycleStop | undefined;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=lifecycle-stop.d.ts.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { isRecord } from "./work-custody-record.js";
|
|
2
|
+
const ABORT_SOURCES = {
|
|
3
|
+
"user-abort": "user",
|
|
4
|
+
"system-abort": "system",
|
|
5
|
+
"provider-abort": "provider",
|
|
6
|
+
"unknown-abort": null,
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* First match wins: the run's own deadline explains its system abort, a guard explains a refused continuation, then
|
|
10
|
+
* the classified abort, and `no_wake` only for a stall no hold or guard owns. Provider failures keep the failed path.
|
|
11
|
+
*/
|
|
12
|
+
export function stopDetail(input, facts, deadline, stalled) {
|
|
13
|
+
if (input?.endedBy === "provider-error" || input?.endedBy === "policy-rejection")
|
|
14
|
+
return undefined;
|
|
15
|
+
if (deadline)
|
|
16
|
+
return {
|
|
17
|
+
cause: "deadline",
|
|
18
|
+
deadline: deadline.kind,
|
|
19
|
+
at: deadline.at,
|
|
20
|
+
...(deadline.persisted === false ? { persisted: false } : {}),
|
|
21
|
+
};
|
|
22
|
+
if (facts?.guard)
|
|
23
|
+
return { cause: "goal_guard", guard: facts.guard.reason, at: new Date(facts.guard.at).toISOString() };
|
|
24
|
+
if (facts?.todo === "capped" || facts?.todo === "cap")
|
|
25
|
+
return { cause: "todo_guard", todo: "cap" };
|
|
26
|
+
const abortSource = input ? ABORT_SOURCES[input.endedBy] : undefined;
|
|
27
|
+
if (abortSource !== undefined)
|
|
28
|
+
return { cause: "aborted", ...(abortSource === null ? {} : { abortSource }) };
|
|
29
|
+
if (stalled && !input?.deadlineStop && !input?.budgetSpent)
|
|
30
|
+
return { cause: "no_wake" };
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
export function sameStop(stop, detail) {
|
|
34
|
+
return (stop.cause === detail.cause &&
|
|
35
|
+
stop.deadline === detail.deadline &&
|
|
36
|
+
stop.guard === detail.guard &&
|
|
37
|
+
stop.todo === detail.todo &&
|
|
38
|
+
stop.abortSource === detail.abortSource &&
|
|
39
|
+
stop.persisted === detail.persisted);
|
|
40
|
+
}
|
|
41
|
+
/** The latest guard appended within one bound end's extension-processing window, including legacy entries. */
|
|
42
|
+
export function goalGuardAfter(branch, guardStartLeafId) {
|
|
43
|
+
const start = guardStartLeafId === null ? -1 : branch.findIndex((entry) => entry.id === guardStartLeafId);
|
|
44
|
+
if (guardStartLeafId !== null && start < 0)
|
|
45
|
+
return undefined;
|
|
46
|
+
for (let index = branch.length - 1; index > start; index--) {
|
|
47
|
+
const entry = branch[index];
|
|
48
|
+
if (entry?.type !== "custom" || entry.customType !== "goal-continuation-stopped" || !isRecord(entry.data))
|
|
49
|
+
continue;
|
|
50
|
+
const { reason, at } = entry.data;
|
|
51
|
+
return typeof reason === "string" && typeof at === "number" ? { reason, at } : undefined;
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
/** Malformed optional detail is dropped; it never invalidates the record that carries it. */
|
|
56
|
+
export function readLastStop(value, runId) {
|
|
57
|
+
if (!isRecord(value) ||
|
|
58
|
+
typeof value.runId !== "string" ||
|
|
59
|
+
value.runId !== runId ||
|
|
60
|
+
typeof value.at !== "string" ||
|
|
61
|
+
typeof value.epoch !== "number")
|
|
62
|
+
return undefined;
|
|
63
|
+
const base = { runId: value.runId, at: value.at, epoch: value.epoch };
|
|
64
|
+
switch (value.cause) {
|
|
65
|
+
case "deadline":
|
|
66
|
+
return value.deadline === "inactivity" || value.deadline === "max_runtime"
|
|
67
|
+
? {
|
|
68
|
+
...base,
|
|
69
|
+
cause: value.cause,
|
|
70
|
+
deadline: value.deadline,
|
|
71
|
+
...(value.persisted === false ? { persisted: false } : {}),
|
|
72
|
+
}
|
|
73
|
+
: undefined;
|
|
74
|
+
case "goal_guard":
|
|
75
|
+
return { ...base, cause: value.cause, ...(typeof value.guard === "string" ? { guard: value.guard } : {}) };
|
|
76
|
+
case "todo_guard":
|
|
77
|
+
return { ...base, cause: value.cause, todo: "cap" };
|
|
78
|
+
case "aborted":
|
|
79
|
+
return {
|
|
80
|
+
...base,
|
|
81
|
+
cause: value.cause,
|
|
82
|
+
...(value.abortSource === "user" || value.abortSource === "system" || value.abortSource === "provider"
|
|
83
|
+
? { abortSource: value.abortSource }
|
|
84
|
+
: {}),
|
|
85
|
+
};
|
|
86
|
+
case "no_wake":
|
|
87
|
+
return { ...base, cause: value.cause };
|
|
88
|
+
default:
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=lifecycle-stop.js.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { MrtrInputResponse } from "./extensions/builtin/mcp/modern/elicitation.ts";
|
|
2
|
+
import type { ValidatedMcpFormSchema } from "./extensions/builtin/mcp/modern/form-schema.ts";
|
|
3
|
+
export interface McpFormScope {
|
|
4
|
+
readonly hostInstance: string;
|
|
5
|
+
readonly durableSessionId: string;
|
|
6
|
+
readonly bindingEpoch: string;
|
|
7
|
+
readonly attachmentId: string;
|
|
8
|
+
readonly attachmentEpoch: string;
|
|
9
|
+
readonly connectionId: string;
|
|
10
|
+
readonly connectionEpoch: string;
|
|
11
|
+
readonly presenterPrincipalId: string;
|
|
12
|
+
readonly presenterGeneration: string;
|
|
13
|
+
readonly presentationBindingId: string;
|
|
14
|
+
readonly presenterKind: "desktop" | "gateway" | "local_tui";
|
|
15
|
+
readonly purpose: "mcp_form";
|
|
16
|
+
}
|
|
17
|
+
export interface McpFormEnvelope {
|
|
18
|
+
readonly sessionId: string;
|
|
19
|
+
readonly bindingEpoch: string;
|
|
20
|
+
readonly attachmentEpoch: string;
|
|
21
|
+
readonly grantId: string;
|
|
22
|
+
readonly dialogId: string;
|
|
23
|
+
readonly dialogToken: string;
|
|
24
|
+
readonly formRevision: string;
|
|
25
|
+
}
|
|
26
|
+
export interface McpFormRequest extends McpFormEnvelope {
|
|
27
|
+
readonly type: "mcp_form_request";
|
|
28
|
+
readonly server: {
|
|
29
|
+
readonly label: string;
|
|
30
|
+
readonly source: "user" | "project" | "plugin";
|
|
31
|
+
};
|
|
32
|
+
readonly message: string;
|
|
33
|
+
readonly schema: ValidatedMcpFormSchema;
|
|
34
|
+
readonly deadlineAtMs: number;
|
|
35
|
+
}
|
|
36
|
+
export interface McpFormResolved {
|
|
37
|
+
readonly type: "mcp_form_resolved";
|
|
38
|
+
readonly sessionId: string;
|
|
39
|
+
readonly grantId: string;
|
|
40
|
+
readonly dialogId: string;
|
|
41
|
+
readonly reason: "answered" | "declined" | "cancelled" | "processing_failed" | "operation_aborted";
|
|
42
|
+
}
|
|
43
|
+
export type McpFormOutcome = ({
|
|
44
|
+
readonly kind: "result";
|
|
45
|
+
readonly result: MrtrInputResponse;
|
|
46
|
+
} | {
|
|
47
|
+
readonly kind: "processing_failure" | "operation_aborted";
|
|
48
|
+
}) & ({
|
|
49
|
+
readonly source: "presenter_action";
|
|
50
|
+
readonly humanAdmission: true;
|
|
51
|
+
} | {
|
|
52
|
+
readonly source: "host_termination";
|
|
53
|
+
readonly humanAdmission: false;
|
|
54
|
+
});
|
|
55
|
+
export interface McpFormInput {
|
|
56
|
+
readonly operationId: string;
|
|
57
|
+
readonly inputId: string;
|
|
58
|
+
readonly round: number;
|
|
59
|
+
readonly server: McpFormRequest["server"];
|
|
60
|
+
readonly message: string;
|
|
61
|
+
readonly schema: ValidatedMcpFormSchema;
|
|
62
|
+
readonly operationSignal: AbortSignal;
|
|
63
|
+
/** Host reads a worker-owned atomic abort fence at the terminal latch, without an IPC round trip. */
|
|
64
|
+
readonly operationCurrent?: () => boolean;
|
|
65
|
+
readonly presentationSignal: AbortSignal;
|
|
66
|
+
/** Remaining UI budget, measured by the host's monotonic clock. */
|
|
67
|
+
readonly deadlineMs: number;
|
|
68
|
+
}
|
|
69
|
+
export interface McpFormPresentation {
|
|
70
|
+
readonly signal: AbortSignal;
|
|
71
|
+
readonly ready: () => boolean;
|
|
72
|
+
readonly present: (input: McpFormInput) => Promise<McpFormOutcome>;
|
|
73
|
+
/** Worker-backed presenters recheck host readiness before each network attempt. */
|
|
74
|
+
readonly synchronize?: () => Promise<void>;
|
|
75
|
+
}
|
|
76
|
+
export interface McpFormBinding {
|
|
77
|
+
readonly epoch: string;
|
|
78
|
+
readonly signal: AbortSignal;
|
|
79
|
+
readonly capture: () => McpFormPresentation | undefined;
|
|
80
|
+
readonly dispose: () => void;
|
|
81
|
+
readonly version?: () => string | undefined;
|
|
82
|
+
readonly subscribe?: (listener: () => void) => () => void;
|
|
83
|
+
}
|
|
84
|
+
export declare function bindMcpFormPresented(options: object, callback: (() => void) | undefined): void;
|
|
85
|
+
export declare function markMcpFormPresented(options: object | undefined): void;
|
|
86
|
+
/** An enforced binding is part of the host-issued presenter identity, not a UI capability declaration. */
|
|
87
|
+
export declare function bindPrivateMcpForms(ui: object, binding: McpFormBinding): void;
|
|
88
|
+
export declare function privateMcpForms(ui: object): McpFormBinding | undefined;
|
|
89
|
+
//# sourceMappingURL=mcp-form-types.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getMcpMrtrPresentation, registerMcpMrtrPresentation } from "./mcp-mrtr-presentations.js";
|
|
2
|
+
const presentedCallbacks = new WeakMap();
|
|
3
|
+
export function bindMcpFormPresented(options, callback) {
|
|
4
|
+
if (callback)
|
|
5
|
+
presentedCallbacks.set(options, callback);
|
|
6
|
+
else
|
|
7
|
+
presentedCallbacks.delete(options);
|
|
8
|
+
}
|
|
9
|
+
export function markMcpFormPresented(options) {
|
|
10
|
+
if (!options)
|
|
11
|
+
return;
|
|
12
|
+
const callback = presentedCallbacks.get(options);
|
|
13
|
+
presentedCallbacks.delete(options);
|
|
14
|
+
callback?.();
|
|
15
|
+
}
|
|
16
|
+
/** An enforced binding is part of the host-issued presenter identity, not a UI capability declaration. */
|
|
17
|
+
export function bindPrivateMcpForms(ui, binding) {
|
|
18
|
+
registerMcpMrtrPresentation(ui, binding.signal, undefined, binding);
|
|
19
|
+
}
|
|
20
|
+
export function privateMcpForms(ui) {
|
|
21
|
+
return getMcpMrtrPresentation(ui)?.privateForms;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=mcp-form-types.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ExtensionUIContext } from "./extensions/types.ts";
|
|
2
|
+
import type { McpFormBinding } from "./mcp-form-types.ts";
|
|
3
|
+
/** Local host references only. RPC capabilities and serialized UI objects cannot enter this registry. */
|
|
4
|
+
export interface McpMrtrPresentation {
|
|
5
|
+
readonly ui: Pick<ExtensionUIContext, "select" | "input">;
|
|
6
|
+
readonly signal: AbortSignal;
|
|
7
|
+
readonly admit?: () => void;
|
|
8
|
+
readonly presented?: () => void;
|
|
9
|
+
readonly privateForms?: McpFormBinding;
|
|
10
|
+
}
|
|
11
|
+
export declare function registerMcpMrtrPresentation(ui: ExtensionUIContext, signal: AbortSignal, admit?: () => void, privateForms?: McpFormBinding): ExtensionUIContext;
|
|
12
|
+
export declare function getMcpMrtrPresentation(ui: object): McpMrtrPresentation | undefined;
|
|
13
|
+
/** Preserve host identity across runner wrapping; private dialogs still use the raw host UI. */
|
|
14
|
+
export declare function inheritMcpMrtrPresentation(source: ExtensionUIContext, wrapper: ExtensionUIContext): ExtensionUIContext;
|
|
15
|
+
//# sourceMappingURL=mcp-mrtr-presentations.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const presentations = new WeakMap();
|
|
2
|
+
export function registerMcpMrtrPresentation(ui, signal, admit, privateForms) {
|
|
3
|
+
presentations.set(ui, { ui, signal, admit, privateForms });
|
|
4
|
+
return ui;
|
|
5
|
+
}
|
|
6
|
+
export function getMcpMrtrPresentation(ui) {
|
|
7
|
+
const presentation = presentations.get(ui);
|
|
8
|
+
return presentation?.signal.aborted ? undefined : presentation;
|
|
9
|
+
}
|
|
10
|
+
/** Preserve host identity across runner wrapping; private dialogs still use the raw host UI. */
|
|
11
|
+
export function inheritMcpMrtrPresentation(source, wrapper) {
|
|
12
|
+
const presentation = getMcpMrtrPresentation(source);
|
|
13
|
+
if (presentation)
|
|
14
|
+
presentations.set(wrapper, presentation);
|
|
15
|
+
return wrapper;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=mcp-mrtr-presentations.js.map
|
|
@@ -22,6 +22,20 @@ export interface LifecycleHold {
|
|
|
22
22
|
readonly kind: "session_hold" | "run_fence" | "inherited_hold" | "unattended";
|
|
23
23
|
readonly since: string;
|
|
24
24
|
}
|
|
25
|
+
/** Why the last run stopped, for that run only (`runId`); cleared when the next lifecycle run starts. */
|
|
26
|
+
export interface LifecycleStop {
|
|
27
|
+
readonly cause: "deadline" | "goal_guard" | "todo_guard" | "aborted" | "no_wake";
|
|
28
|
+
readonly runId: string;
|
|
29
|
+
readonly at: string;
|
|
30
|
+
readonly epoch: number;
|
|
31
|
+
readonly deadline?: "inactivity" | "max_runtime";
|
|
32
|
+
/** The goal guard reason exactly as `goal-continuation-stopped` recorded it. */
|
|
33
|
+
readonly guard?: string;
|
|
34
|
+
readonly todo?: "cap";
|
|
35
|
+
readonly abortSource?: "user" | "system" | "provider";
|
|
36
|
+
/** Present only when the deadline-stop entry write failed; distinct from the snapshot-level `persisted`. */
|
|
37
|
+
readonly persisted?: false;
|
|
38
|
+
}
|
|
25
39
|
export interface SpawnedBy {
|
|
26
40
|
readonly parentSessionId: string;
|
|
27
41
|
readonly rootSessionId: string;
|
|
@@ -48,6 +62,7 @@ export interface SessionLifecycle {
|
|
|
48
62
|
readonly taskId?: string;
|
|
49
63
|
readonly waitingOn?: LifecycleWaits;
|
|
50
64
|
readonly holds?: readonly LifecycleHold[];
|
|
65
|
+
readonly lastStop?: LifecycleStop;
|
|
51
66
|
readonly completion?: {
|
|
52
67
|
readonly kind: "goal" | "run" | "agent" | "reply";
|
|
53
68
|
readonly at: string;
|
|
@@ -2,8 +2,10 @@ import { type TurnEndLivenessInput } from "./extensions/builtin/goal/turn-end-li
|
|
|
2
2
|
import type { Goal } from "./extensions/builtin/goal/types.ts";
|
|
3
3
|
import { LifecycleChildren } from "./lifecycle-children.ts";
|
|
4
4
|
import { LifecycleLedger, type LifecyclePersistence } from "./lifecycle-ledger.ts";
|
|
5
|
+
import { type RunStopFacts } from "./lifecycle-stop.ts";
|
|
5
6
|
import type { SessionActivitySnapshot } from "./session-activity.ts";
|
|
6
7
|
import type { ChildLifecycleReport, HumanWait, LifecycleHold, SessionLifecycle } from "./session-lifecycle-types.ts";
|
|
8
|
+
import type { TurnDeadlineKind } from "./turn-deadline.ts";
|
|
7
9
|
import type { WorkCustodyRecord } from "./work-custody-record.ts";
|
|
8
10
|
import { SessionWorkObligations } from "./work-obligations.ts";
|
|
9
11
|
export interface LifecycleRuntimeFacts {
|
|
@@ -33,6 +35,10 @@ export declare class SessionLifecycleController {
|
|
|
33
35
|
private ownTask;
|
|
34
36
|
private evaluating;
|
|
35
37
|
private deferred;
|
|
38
|
+
private stopFacts;
|
|
39
|
+
private deadlineStop;
|
|
40
|
+
/** A run that ended before this process keeps exactly the stop its record carries; none is derived for it. */
|
|
41
|
+
private readonly restoredRunId;
|
|
36
42
|
constructor(options: SessionLifecycleOptions);
|
|
37
43
|
get snapshot(): SessionLifecycle;
|
|
38
44
|
get lastTurnInput(): TurnEndLivenessInput | undefined;
|
|
@@ -45,12 +51,22 @@ export declare class SessionLifecycleController {
|
|
|
45
51
|
admissionSettled(): void;
|
|
46
52
|
startRun(origin?: "human" | "automatic"): void;
|
|
47
53
|
idle(): void;
|
|
48
|
-
|
|
54
|
+
/** The turn deadline fired: captured before its durable write, for the run it stopped only. */
|
|
55
|
+
deadlineStopped(kind: TurnDeadlineKind, atMs: number): void;
|
|
56
|
+
/** The deadline-stop entry was refused; the captured stop says so, and the snapshot's `persisted` is untouched. */
|
|
57
|
+
deadlineStopWriteFailed(): void;
|
|
58
|
+
goalFacts(goal: Goal | null, input?: TurnEndLivenessInput, stop?: RunStopFacts): void;
|
|
49
59
|
goalChanged(goal: Goal | null): void;
|
|
50
60
|
reportChild(report: ChildLifecycleReport): void;
|
|
51
61
|
requestSelfSettle(): import("./session-lifecycle-types.ts").SelfSettleResult;
|
|
52
62
|
markRunCompleted(runId: string): void;
|
|
53
63
|
recompute(): void;
|
|
54
64
|
private evaluate;
|
|
65
|
+
/**
|
|
66
|
+
* Derived only from the current run's own end, bound when its agent_end was emitted: an end processed after a
|
|
67
|
+
* later run started sets nothing, and so does a fact refresh that carries no bound turn end, even when the goal
|
|
68
|
+
* collector still returns the previous run's end. An unchanged stop keeps its first `at`.
|
|
69
|
+
*/
|
|
70
|
+
private lastStop;
|
|
55
71
|
}
|
|
56
72
|
//# sourceMappingURL=session-lifecycle.d.ts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { effectiveWakes } from "./extensions/builtin/goal/turn-end-liveness.js";
|
|
2
2
|
import { LifecycleChildren } from "./lifecycle-children.js";
|
|
3
3
|
import { LifecycleLedger } from "./lifecycle-ledger.js";
|
|
4
|
+
import { sameStop, stopDetail } from "./lifecycle-stop.js";
|
|
4
5
|
import { lifecycleState, lifecycleTerminal } from "./session-lifecycle-reducer.js";
|
|
5
6
|
import { SessionWorkObligations } from "./work-obligations.js";
|
|
6
7
|
/** One event-driven reducer per session, shared by every client surface. */
|
|
@@ -21,6 +22,7 @@ export class SessionLifecycleController {
|
|
|
21
22
|
options.append(record);
|
|
22
23
|
},
|
|
23
24
|
});
|
|
25
|
+
this.restoredRunId = this.ledger.runId;
|
|
24
26
|
if (this.ledger.lastTurnEnd)
|
|
25
27
|
this.liveness = {
|
|
26
28
|
...this.ledger.lastTurnEnd,
|
|
@@ -94,6 +96,8 @@ export class SessionLifecycleController {
|
|
|
94
96
|
this.runAdmitted = false;
|
|
95
97
|
this.idleBoundaryPassed = false;
|
|
96
98
|
this.liveness = undefined;
|
|
99
|
+
this.stopFacts = undefined;
|
|
100
|
+
this.deadlineStop = undefined;
|
|
97
101
|
const interruptedRun = this.ledger.interrupted ? this.ledger.runId : undefined;
|
|
98
102
|
this.ledger.interrupted = false;
|
|
99
103
|
this.ledger.startRun(origin ?? (this.pendingHuman ? "human" : "automatic"));
|
|
@@ -115,8 +119,20 @@ export class SessionLifecycleController {
|
|
|
115
119
|
if (seq > 0 && this.ledger.snapshot.seq === seq)
|
|
116
120
|
this.ledger.checkpoint();
|
|
117
121
|
}
|
|
118
|
-
|
|
122
|
+
/** The turn deadline fired: captured before its durable write, for the run it stopped only. */
|
|
123
|
+
deadlineStopped(kind, atMs) {
|
|
124
|
+
const runId = this.ledger.runId;
|
|
125
|
+
if (this.ledger.runActive && runId)
|
|
126
|
+
this.deadlineStop = { runId, kind, at: new Date(atMs).toISOString() };
|
|
127
|
+
}
|
|
128
|
+
/** The deadline-stop entry was refused; the captured stop says so, and the snapshot's `persisted` is untouched. */
|
|
129
|
+
deadlineStopWriteFailed() {
|
|
130
|
+
if (this.deadlineStop)
|
|
131
|
+
this.deadlineStop = { ...this.deadlineStop, persisted: false };
|
|
132
|
+
}
|
|
133
|
+
goalFacts(goal, input, stop) {
|
|
119
134
|
this.liveness = input;
|
|
135
|
+
this.stopFacts = stop;
|
|
120
136
|
if (input)
|
|
121
137
|
this.ledger.lastTurnEnd = {
|
|
122
138
|
endedBy: input.endedBy,
|
|
@@ -282,6 +298,7 @@ export class SessionLifecycleController {
|
|
|
282
298
|
}
|
|
283
299
|
: undefined;
|
|
284
300
|
const waits = { children, background, human };
|
|
301
|
+
const lastStop = this.lastStop(input, synthetic.some((wait) => wait.kind === "stalled"));
|
|
285
302
|
const state = lifecycleState({
|
|
286
303
|
role: this.ledger.snapshot.role,
|
|
287
304
|
terminal: this.ownTask?.terminal,
|
|
@@ -296,7 +313,28 @@ export class SessionLifecycleController {
|
|
|
296
313
|
this.ownTask?.terminal === undefined &&
|
|
297
314
|
(!this.idleBoundaryPassed || active.length > 0))
|
|
298
315
|
return;
|
|
299
|
-
this.ledger.update({ state, waits, holds: runtime.holds, failure });
|
|
316
|
+
this.ledger.update({ state, waits, holds: runtime.holds, failure, lastStop });
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Derived only from the current run's own end, bound when its agent_end was emitted: an end processed after a
|
|
320
|
+
* later run started sets nothing, and so does a fact refresh that carries no bound turn end, even when the goal
|
|
321
|
+
* collector still returns the previous run's end. An unchanged stop keeps its first `at`.
|
|
322
|
+
*/
|
|
323
|
+
lastStop(input, stalled) {
|
|
324
|
+
const existing = this.ledger.snapshot.lastStop;
|
|
325
|
+
const runId = this.ledger.runId;
|
|
326
|
+
if (!runId || runId === this.restoredRunId)
|
|
327
|
+
return existing;
|
|
328
|
+
if (this.stopFacts?.runId !== runId)
|
|
329
|
+
return existing;
|
|
330
|
+
const deadline = input && this.deadlineStop?.runId === runId ? this.deadlineStop : undefined;
|
|
331
|
+
const detail = stopDetail(input, this.stopFacts, deadline, stalled);
|
|
332
|
+
if (!detail)
|
|
333
|
+
return existing;
|
|
334
|
+
if (existing && sameStop(existing, detail))
|
|
335
|
+
return existing;
|
|
336
|
+
const { at, ...cause } = detail;
|
|
337
|
+
return { ...cause, runId, epoch: this.ledger.snapshot.epoch, at: at ?? new Date().toISOString() };
|
|
300
338
|
}
|
|
301
339
|
}
|
|
302
340
|
//# sourceMappingURL=session-lifecycle.js.map
|
package/dist/main.js
CHANGED
|
@@ -1067,7 +1067,8 @@ export async function main(args, options) {
|
|
|
1067
1067
|
if (appMode === "rpc" && parsed.multiSession) {
|
|
1068
1068
|
if (options?.extensionFactories?.length)
|
|
1069
1069
|
throw new Error("Shared RPC workers require file-backed extensions; inline factories cannot cross isolates");
|
|
1070
|
-
const
|
|
1070
|
+
const { presenterBootstrapFd, mcpPresenterEnforcement, ...sessionArgs } = parsed;
|
|
1071
|
+
const runtimeConfiguration = { parsed: sessionArgs, cwd, agentDir, appMode, toolRuntime };
|
|
1071
1072
|
// Socket hosts (the machine-wide daemon) run every session IN this process:
|
|
1072
1073
|
// createHostCore selects the worker registry only when a workerConfiguration
|
|
1073
1074
|
// is passed, so withholding it is what selects the uncapped in-process registry.
|
|
@@ -1098,6 +1099,8 @@ export async function main(args, options) {
|
|
|
1098
1099
|
}
|
|
1099
1100
|
printTimings();
|
|
1100
1101
|
await runMultiSessionHost({
|
|
1102
|
+
presenterBootstrapFd,
|
|
1103
|
+
mcpPresenterEnforcement,
|
|
1101
1104
|
agentDir,
|
|
1102
1105
|
createRuntime: createCliRuntimeFactory(runtimeConfiguration, {
|
|
1103
1106
|
...(hostModelRuntime === undefined ? {} : { modelRuntime: hostModelRuntime }),
|
|
@@ -1292,6 +1295,7 @@ export async function main(args, options) {
|
|
|
1292
1295
|
// mode seam: interactive startup still loads the same module before first use.
|
|
1293
1296
|
const { InteractiveMode } = await import("./modes/interactive/interactive-mode.js");
|
|
1294
1297
|
const interactiveMode = new InteractiveMode(runtime, {
|
|
1298
|
+
mcpPresenterEnforcement: parsed.mcpPresenterEnforcement,
|
|
1295
1299
|
migratedProviders,
|
|
1296
1300
|
authNotices: interactiveAuthNotices(services.authStorage),
|
|
1297
1301
|
legacyPiEditNotice: legacyPiEditStartupNotice(),
|
|
@@ -21,6 +21,7 @@ export declare class ExtensionSelectorComponent extends Container {
|
|
|
21
21
|
private countdown;
|
|
22
22
|
private onToggleToolsExpanded;
|
|
23
23
|
private maxVisibleOptions;
|
|
24
|
+
private answerSettled;
|
|
24
25
|
constructor(title: string, options: string[], onSelect: (option: string) => void, onCancel: () => void, opts?: ExtensionSelectorOptions);
|
|
25
26
|
private updateList;
|
|
26
27
|
handleInput(keyData: string): void;
|
|
@@ -12,6 +12,7 @@ export class ExtensionSelectorComponent extends Container {
|
|
|
12
12
|
constructor(title, options, onSelect, onCancel, opts) {
|
|
13
13
|
super();
|
|
14
14
|
this.selectedIndex = 0;
|
|
15
|
+
this.answerSettled = false;
|
|
15
16
|
this.options = options;
|
|
16
17
|
this.onSelectCallback = onSelect;
|
|
17
18
|
this.onCancelCallback = onCancel;
|
|
@@ -67,6 +68,8 @@ export class ExtensionSelectorComponent extends Container {
|
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
handleInput(keyData) {
|
|
71
|
+
if (this.answerSettled)
|
|
72
|
+
return;
|
|
70
73
|
const kb = getKeybindings();
|
|
71
74
|
if (kb.matches(keyData, "app.tools.expand")) {
|
|
72
75
|
this.onToggleToolsExpanded?.();
|
|
@@ -89,6 +92,7 @@ export class ExtensionSelectorComponent extends Container {
|
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
dispose() {
|
|
95
|
+
this.answerSettled = true;
|
|
92
96
|
this.countdown?.dispose();
|
|
93
97
|
}
|
|
94
98
|
}
|
|
@@ -56,6 +56,8 @@ interface InteractiveUserInput {
|
|
|
56
56
|
* Options for InteractiveMode initialization.
|
|
57
57
|
*/
|
|
58
58
|
export interface InteractiveModeOptions {
|
|
59
|
+
/** Opt in to host-issued MCP form authority; ordinary terminal forms remain the default. */
|
|
60
|
+
mcpPresenterEnforcement?: boolean;
|
|
59
61
|
/** Renderer mode for the interactive terminal. */
|
|
60
62
|
uiMode?: TuiMode;
|
|
61
63
|
/** Providers that were migrated to auth.json (shows warning) */
|
|
@@ -327,6 +329,8 @@ export declare class InteractiveMode {
|
|
|
327
329
|
*/
|
|
328
330
|
private unsubscribeSecurityNotices?;
|
|
329
331
|
private detachMcpGrantPresentation?;
|
|
332
|
+
private mcpMrtrPresentationLifetime;
|
|
333
|
+
private mcpLocalPresenter?;
|
|
330
334
|
private bindCurrentSessionExtensions;
|
|
331
335
|
private applyFullscreenScrollbarSetting;
|
|
332
336
|
private applyRuntimeSettings;
|