@code-yeongyu/senpi 2026.11.10-1 → 2026.11.10-2
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/{chunk-GPXZTSEX.js → chunk-2M54EAAY.js} +3 -3
- package/dist/bundle/chunks/{chunk-YPAQXRP3.js → chunk-47T6YMI2.js} +2 -2
- package/dist/bundle/chunks/{chunk-I5ASHSLZ.js → chunk-4JMGWVRH.js} +1 -1
- package/dist/bundle/chunks/{chunk-HI5JSLG5.js → chunk-74IB3JPP.js} +2 -2
- package/dist/bundle/chunks/{chunk-NH7SYIGN.js → chunk-DLPV4B3R.js} +1 -1
- package/dist/bundle/chunks/{chunk-Y7Z7XPUF.js → chunk-DZQA5LVN.js} +1 -1
- package/dist/bundle/chunks/{chunk-2CNSCMYC.js → chunk-E5O4SP5I.js} +1 -1
- package/dist/bundle/chunks/{chunk-3IYWH6RO.js → chunk-IKJCA5SH.js} +1 -1
- package/dist/bundle/chunks/{chunk-H33A33M4.js → chunk-M2VJVJMK.js} +1 -1
- package/dist/bundle/chunks/{chunk-L6NRDUWJ.js → chunk-OF3XVGG4.js} +1 -1
- package/dist/bundle/chunks/{chunk-THYEZBUG.js → chunk-Q4G5DECT.js} +2 -2
- package/dist/bundle/chunks/{chunk-ARUV3US4.js → chunk-QATU6BBC.js} +1 -1
- package/dist/bundle/chunks/{chunk-FAFIGBUD.js → chunk-TUEZNIBK.js} +1 -1
- package/dist/bundle/chunks/{chunk-BELNOPAI.js → chunk-UOZDDYBN.js} +2 -2
- package/dist/bundle/chunks/{chunk-DBQBJTMF.js → chunk-VUY4BZDI.js} +1 -1
- package/dist/bundle/chunks/{chunk-TGW4R7SI.js → chunk-XH24X5B2.js} +1 -1
- package/dist/bundle/chunks/chunk-XI6THO4F.js +2 -0
- package/dist/bundle/chunks/{chunk-X4XWAB3J.js → chunk-YCPURLPU.js} +38 -38
- package/dist/bundle/chunks/{chunk-LHEP3ACI.js → chunk-YNGEZBTP.js} +1 -1
- package/dist/bundle/chunks/{cli-main-VG3C2EXI.js → cli-main-Y6ALA5OI.js} +1 -1
- package/dist/bundle/chunks/{hand-entry-3LQKUTYS.js → hand-entry-IPDAIOGI.js} +1 -1
- package/dist/bundle/chunks/{host-command-CCASS7W4.js → host-command-2326GCUP.js} +1 -1
- package/dist/bundle/chunks/{host-lifecycle-WWC7HXQ6.js → host-lifecycle-TNHASSFJ.js} +1 -1
- package/dist/bundle/chunks/host-lifecycle.js +1 -1
- package/dist/bundle/chunks/{host-session-command-YJB2FLKU.js → host-session-command-BZYBDOIE.js} +1 -1
- package/dist/bundle/chunks/{host-socket-request-DMYXC56U.js → host-socket-request-QN4Q2P5E.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-CUN3WMUF.js → interactive-mode-LBQB7DDR.js} +1 -1
- package/dist/bundle/chunks/{main-P7XRC2OI.js → main-FFRFP34W.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-244X3P6L.js → multi-session-host-NDIHHJ2L.js} +11 -8
- package/dist/bundle/chunks/{package-manager-cli-UVWJOQA3.js → package-manager-cli-XCPAMOYI.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-G4I3HHPB.js → rpc-mode-TKNZM4PZ.js} +1 -1
- package/dist/bundle/chunks/{rpc-monitor-source-C6VQ3BC4.js → rpc-monitor-source-PGTDSGWP.js} +1 -1
- package/dist/bundle/chunks/{run-attached-tui-R3RB5P7G.js → run-attached-tui-JDUKXDI2.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-5FG2OKSR.js → schedule-command-HF2DHABB.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-XWCFLJYC.js → session-control-endpoint-A6R6W2JQ.js} +1 -1
- package/dist/bundle/chunks/{session-picker-2FZSNFS7.js → session-picker-GWA4S2PO.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +119 -116
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/host-supervisor.js +1 -1
- 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/core/agent-session-runtime.d.ts +2 -0
- package/dist/core/agent-session-runtime.js +5 -0
- package/dist/core/agent-session.d.ts +1 -1
- package/dist/core/agent-session.js +30 -10
- package/dist/core/extensions/builtin/config-reload/change-groups.js +6 -2
- package/dist/core/extensions/builtin/mcp/auth/catalog-identity.js +32 -8
- package/dist/core/extensions/builtin/mcp/auth/oauth-grant.d.ts +43 -0
- package/dist/core/extensions/builtin/mcp/auth/oauth-grant.js +196 -0
- package/dist/core/extensions/builtin/mcp/auth/oauth-provider.d.ts +4 -17
- package/dist/core/extensions/builtin/mcp/auth/oauth-provider.js +64 -75
- package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.d.ts +1 -1
- package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.js +25 -4
- package/dist/core/extensions/builtin/mcp/auth/oauth.js +10 -7
- package/dist/core/extensions/builtin/mcp/auth/token-store.d.ts +18 -0
- package/dist/core/extensions/builtin/mcp/auth/token-store.js +3 -0
- package/dist/core/extensions/builtin/mcp/catalog-cache.js +7 -6
- package/dist/core/extensions/builtin/mcp/catalog.js +3 -2
- package/dist/core/extensions/builtin/mcp/invocation.js +6 -0
- package/dist/core/extensions/builtin/mcp/service-register.js +16 -2
- package/dist/core/extensions/builtin/mcp/service-tools-changed.js +61 -54
- package/dist/core/extensions/builtin/mcp/startup-race.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/startup-race.js +25 -7
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-2.js +6 -6
- package/dist/core/open-timing.d.ts +2 -0
- package/dist/core/open-timing.js +7 -1
- package/dist/core/session-manager.d.ts +2 -0
- package/dist/core/session-manager.js +15 -1
- package/dist/modes/interactive/session-control-rpc-commands.d.ts +1 -1
- package/dist/modes/interactive/session-control-rpc-commands.js +5 -0
- package/dist/modes/rpc/compact-admission-validation.js +4 -1
- package/dist/modes/rpc/connection-handler.d.ts +2 -0
- package/dist/modes/rpc/connection-handler.js +10 -2
- package/dist/modes/rpc/custom-capability.d.ts +2 -0
- package/dist/modes/rpc/custom-capability.js +2 -0
- package/dist/modes/rpc/effect-commands.d.ts +21 -0
- package/dist/modes/rpc/host-capabilities.d.ts +1 -0
- package/dist/modes/rpc/host-capabilities.js +3 -1
- package/dist/modes/rpc/host-observe-request.d.ts +2 -0
- package/dist/modes/rpc/host-observe-request.js +6 -1
- package/dist/modes/rpc/host-park-ledger.d.ts +19 -0
- package/dist/modes/rpc/host-park-ledger.js +110 -0
- package/dist/modes/rpc/lifecycle-delivery.js +4 -1
- package/dist/modes/rpc/media-placeholders.js +1 -0
- package/dist/modes/rpc/method-classes-schema-host.d.ts +42 -1
- package/dist/modes/rpc/method-classes-schema-host.js +24 -21
- package/dist/modes/rpc/method-classes-schema.d.ts +42 -1
- package/dist/modes/rpc/method-classes-schema.js +5 -2
- package/dist/modes/rpc/method-classes.d.ts +2 -0
- package/dist/modes/rpc/method-classes.js +3 -0
- package/dist/modes/rpc/multi-session-host.js +1 -1
- package/dist/modes/rpc/rpc-host-command-types.d.ts +14 -1
- package/dist/modes/rpc/rpc-host-response-types.d.ts +21 -0
- package/dist/modes/rpc/session-attachment-gate.js +2 -0
- package/dist/modes/rpc/session-binding.d.ts +1 -0
- package/dist/modes/rpc/session-binding.js +6 -1
- package/dist/modes/rpc/session-connection-context.d.ts +2 -0
- package/dist/modes/rpc/session-connection-context.js +4 -0
- package/dist/modes/rpc/session-registry-attach.d.ts +2 -0
- package/dist/modes/rpc/session-registry-attach.js +15 -11
- package/dist/modes/rpc/session-registry-claims.js +2 -0
- package/dist/modes/rpc/session-registry-open.js +13 -7
- package/dist/modes/rpc/session-registry-opening.js +2 -1
- package/dist/modes/rpc/session-registry-types.d.ts +4 -0
- package/dist/modes/rpc/session-registry.d.ts +3 -0
- package/dist/modes/rpc/session-registry.js +27 -3
- package/dist/modes/rpc/session-router-close.js +1 -0
- package/dist/modes/rpc/session-router-context.d.ts +4 -2
- package/dist/modes/rpc/session-router-context.js +3 -0
- package/dist/modes/rpc/session-router-host-reads.js +1 -0
- package/dist/modes/rpc/session-router-occupancy.js +10 -1
- package/dist/modes/rpc/session-router-open-result.d.ts +12 -0
- package/dist/modes/rpc/session-router-open-result.js +63 -0
- package/dist/modes/rpc/session-router-open.d.ts +3 -0
- package/dist/modes/rpc/session-router-open.js +15 -63
- package/dist/modes/rpc/session-router-park.d.ts +15 -0
- package/dist/modes/rpc/session-router-park.js +176 -0
- package/dist/modes/rpc/session-router-routing.d.ts +1 -1
- package/dist/modes/rpc/session-router-routing.js +7 -0
- package/dist/modes/rpc/session-status-source.js +2 -2
- package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
- package/docs/engine-socket/fixtures/rpc-inbound-samples.json +22 -2
- package/docs/rpc.md +50 -0
- package/native/socket-secret/prebuilds/win32-arm64/senpi_socket_secret.win32-arm64.node +0 -0
- package/native/socket-secret/prebuilds/win32-x64/senpi_socket_secret.win32-x64.node +0 -0
- package/package.json +6 -6
- package/dist/bundle/chunks/chunk-436DNN4Z.js +0 -2
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/** Speculation is bounded independently of the unlimited in-process session registry. */
|
|
2
|
+
const MAX_PARKS = 4;
|
|
3
|
+
const PARK_TTL_MS = 60_000;
|
|
4
|
+
/** Owns both in-flight builds and ready parks, including the races with claim, expiry and shutdown. */
|
|
5
|
+
export class HostParkLedger {
|
|
6
|
+
constructor(close) {
|
|
7
|
+
this.parks = new Map();
|
|
8
|
+
this.replacements = new Map();
|
|
9
|
+
this.stopped = false;
|
|
10
|
+
this.close = close;
|
|
11
|
+
}
|
|
12
|
+
get size() {
|
|
13
|
+
return this.parks.size;
|
|
14
|
+
}
|
|
15
|
+
async park(key, build) {
|
|
16
|
+
const existing = this.parks.get(key);
|
|
17
|
+
if (existing && !existing.claim && !existing.closing) {
|
|
18
|
+
await existing.ready;
|
|
19
|
+
return "already_parked";
|
|
20
|
+
}
|
|
21
|
+
// A park being claimed or closed is on its way out; the replacement scheduled after it builds the next.
|
|
22
|
+
if (existing)
|
|
23
|
+
return "full";
|
|
24
|
+
const replacement = this.replacements.get(key);
|
|
25
|
+
if (replacement !== undefined) {
|
|
26
|
+
clearTimeout(replacement);
|
|
27
|
+
this.replacements.delete(key);
|
|
28
|
+
}
|
|
29
|
+
if (this.stopped)
|
|
30
|
+
throw new Error("host_draining");
|
|
31
|
+
if (this.parks.size + this.replacements.size >= MAX_PARKS)
|
|
32
|
+
return "full";
|
|
33
|
+
const ready = Promise.withResolvers();
|
|
34
|
+
ready.promise.catch(() => { });
|
|
35
|
+
const park = { ready: ready.promise };
|
|
36
|
+
// Reserved before build's first await: a second request joins, never starts another runtime.
|
|
37
|
+
this.parks.set(key, park);
|
|
38
|
+
try {
|
|
39
|
+
park.opened = await build();
|
|
40
|
+
if (!this.stopped) {
|
|
41
|
+
park.expiry = setTimeout(() => void this.retire(key, park), PARK_TTL_MS);
|
|
42
|
+
park.expiry.unref?.();
|
|
43
|
+
}
|
|
44
|
+
ready.resolve();
|
|
45
|
+
if (this.stopped)
|
|
46
|
+
await this.retire(key, park);
|
|
47
|
+
return "parked";
|
|
48
|
+
}
|
|
49
|
+
catch (cause) {
|
|
50
|
+
this.parks.delete(key);
|
|
51
|
+
ready.reject(cause);
|
|
52
|
+
throw cause;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** A claim never waits for a build, and consumes the ready park synchronously. */
|
|
56
|
+
take(key) {
|
|
57
|
+
const park = this.parks.get(key);
|
|
58
|
+
if (this.stopped || !park?.opened || park.claim || park.closing)
|
|
59
|
+
return undefined;
|
|
60
|
+
clearTimeout(park.expiry);
|
|
61
|
+
park.claim = Promise.withResolvers();
|
|
62
|
+
return park.opened;
|
|
63
|
+
}
|
|
64
|
+
finish(key) {
|
|
65
|
+
const park = this.parks.get(key);
|
|
66
|
+
this.parks.delete(key);
|
|
67
|
+
park?.claim?.resolve();
|
|
68
|
+
}
|
|
69
|
+
replace(key, build) {
|
|
70
|
+
if (this.stopped || this.replacements.has(key))
|
|
71
|
+
return;
|
|
72
|
+
const timer = setTimeout(() => {
|
|
73
|
+
this.replacements.delete(key);
|
|
74
|
+
void this.park(key, build).catch((cause) => {
|
|
75
|
+
process.stderr.write(`senpi rpc replacement park failed: ${String(cause)}\n`);
|
|
76
|
+
});
|
|
77
|
+
}, 0);
|
|
78
|
+
timer.unref?.();
|
|
79
|
+
this.replacements.set(key, timer);
|
|
80
|
+
}
|
|
81
|
+
/** Fences new builds synchronously; every build already accepted is closed when it finishes. */
|
|
82
|
+
async stop() {
|
|
83
|
+
this.stopped = true;
|
|
84
|
+
for (const timer of this.replacements.values())
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
this.replacements.clear();
|
|
87
|
+
await Promise.all([...this.parks].map(async ([key, park]) => {
|
|
88
|
+
await park.ready.catch(() => { });
|
|
89
|
+
if (park.claim)
|
|
90
|
+
await park.claim.promise;
|
|
91
|
+
else
|
|
92
|
+
await this.retire(key, park);
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
retire(key, park) {
|
|
96
|
+
clearTimeout(park.expiry);
|
|
97
|
+
park.closing ??= (async () => {
|
|
98
|
+
try {
|
|
99
|
+
if (park.opened)
|
|
100
|
+
await this.close(park.opened);
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
if (this.parks.get(key) === park)
|
|
104
|
+
this.parks.delete(key);
|
|
105
|
+
}
|
|
106
|
+
})();
|
|
107
|
+
return park.closing;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=host-park-ledger.js.map
|
|
@@ -15,7 +15,10 @@ export function observeLifecycleDelivery(record) {
|
|
|
15
15
|
return record;
|
|
16
16
|
if (record.command === "get_state")
|
|
17
17
|
return { ...record, data: observeState(data) };
|
|
18
|
-
if (record.command === "open_session"
|
|
18
|
+
if ((record.command === "open_session" || record.command === "claim_session") &&
|
|
19
|
+
"state" in data &&
|
|
20
|
+
typeof data.state === "object" &&
|
|
21
|
+
data.state !== null)
|
|
19
22
|
return { ...record, data: { ...data, state: observeState(data.state) } };
|
|
20
23
|
return record;
|
|
21
24
|
}
|
|
@@ -15,6 +15,38 @@ export declare const HOST_RECORD_SCHEMAS: {
|
|
|
15
15
|
id: Type.TOptional<Type.TString>;
|
|
16
16
|
admissionId: Type.TOptional<Type.TString>;
|
|
17
17
|
type: Type.TLiteral<"open_session">;
|
|
18
|
+
} & {
|
|
19
|
+
retry: Type.TOptional<Type.TLiteral<true>>;
|
|
20
|
+
attended: Type.TOptional<Type.TLiteral<false>>;
|
|
21
|
+
spawnedBy: Type.TOptional<Type.TObject<{
|
|
22
|
+
parentSessionId: Type.TString;
|
|
23
|
+
rootSessionId: Type.TString;
|
|
24
|
+
taskId: Type.TString;
|
|
25
|
+
}>>;
|
|
26
|
+
expectedHostInstance: Type.TOptional<Type.TString>;
|
|
27
|
+
sessionPath: Type.TOptional<Type.TString>;
|
|
28
|
+
cwd: Type.TOptional<Type.TString>;
|
|
29
|
+
provider: Type.TOptional<Type.TString>;
|
|
30
|
+
modelId: Type.TOptional<Type.TString>;
|
|
31
|
+
thinkingLevel: Type.TOptional<Type.TUnion<[Type.TLiteral<string>, ...Type.TLiteral<string>[]]>>;
|
|
32
|
+
permissionPreset: Type.TOptional<Type.TString>;
|
|
33
|
+
retain_on_disconnect: Type.TOptional<Type.TBoolean>;
|
|
34
|
+
kind: Type.TOptional<Type.TUnion<[Type.TLiteral<string>, ...Type.TLiteral<string>[]]>>;
|
|
35
|
+
context: Type.TOptional<Type.TRecord<"^.*$", Type.TString>>;
|
|
36
|
+
auto_title: Type.TOptional<Type.TBoolean>;
|
|
37
|
+
durableSessionId: Type.TOptional<Type.TString>;
|
|
38
|
+
promptSurface: Type.TOptional<Type.TUnion<[Type.TLiteral<string>, ...Type.TLiteral<string>[]]>>;
|
|
39
|
+
browserEngine: Type.TOptional<Type.TUnion<[Type.TLiteral<string>, ...Type.TLiteral<string>[]]>>;
|
|
40
|
+
retryFallback: Type.TOptional<Type.TObject<{
|
|
41
|
+
modelFallback: Type.TBoolean;
|
|
42
|
+
fallbackChains: Type.TRecord<"^.*$", Type.TImmutable<Type.TArray<Type.TString>>>;
|
|
43
|
+
}>>;
|
|
44
|
+
env: Type.TOptional<Type.TRecord<"^.*$", Type.TString>>;
|
|
45
|
+
}>;
|
|
46
|
+
claim_session: Type.TObject<{
|
|
47
|
+
id: Type.TOptional<Type.TString>;
|
|
48
|
+
admissionId: Type.TOptional<Type.TString>;
|
|
49
|
+
type: Type.TLiteral<"claim_session">;
|
|
18
50
|
} & {
|
|
19
51
|
readonly retry: Type.TOptional<Type.TLiteral<true>>;
|
|
20
52
|
readonly attended: Type.TOptional<Type.TLiteral<false>>;
|
|
@@ -25,7 +57,6 @@ export declare const HOST_RECORD_SCHEMAS: {
|
|
|
25
57
|
}>>;
|
|
26
58
|
readonly expectedHostInstance: Type.TOptional<Type.TString>;
|
|
27
59
|
readonly sessionPath: Type.TOptional<Type.TString>;
|
|
28
|
-
readonly cwd: Type.TOptional<Type.TString>;
|
|
29
60
|
readonly provider: Type.TOptional<Type.TString>;
|
|
30
61
|
readonly modelId: Type.TOptional<Type.TString>;
|
|
31
62
|
readonly thinkingLevel: Type.TOptional<Type.TUnion<[Type.TLiteral<string>, ...Type.TLiteral<string>[]]>>;
|
|
@@ -42,6 +73,16 @@ export declare const HOST_RECORD_SCHEMAS: {
|
|
|
42
73
|
fallbackChains: Type.TRecord<"^.*$", Type.TImmutable<Type.TArray<Type.TString>>>;
|
|
43
74
|
}>>;
|
|
44
75
|
readonly env: Type.TOptional<Type.TRecord<"^.*$", Type.TString>>;
|
|
76
|
+
readonly cwd: Type.TString;
|
|
77
|
+
}>;
|
|
78
|
+
park_session: Type.TObject<{
|
|
79
|
+
id: Type.TOptional<Type.TString>;
|
|
80
|
+
admissionId: Type.TOptional<Type.TString>;
|
|
81
|
+
type: Type.TLiteral<"park_session">;
|
|
82
|
+
} & {
|
|
83
|
+
readonly cwd: Type.TString;
|
|
84
|
+
readonly kind: Type.TOptional<Type.TUnion<[Type.TLiteral<string>, ...Type.TLiteral<string>[]]>>;
|
|
85
|
+
readonly context: Type.TOptional<Type.TRecord<"^.*$", Type.TString>>;
|
|
45
86
|
}>;
|
|
46
87
|
close_session: Type.TObject<{
|
|
47
88
|
id: Type.TOptional<Type.TString>;
|
|
@@ -9,32 +9,35 @@ const uiResponseFrame = {
|
|
|
9
9
|
uiRequestId: OptId,
|
|
10
10
|
answerId: OptId,
|
|
11
11
|
};
|
|
12
|
+
const openProperties = {
|
|
13
|
+
retry: Opt(Type.Literal(true)),
|
|
14
|
+
attended: Opt(Type.Literal(false)),
|
|
15
|
+
spawnedBy: Opt(Type.Object({ parentSessionId: Id, rootSessionId: Id, taskId: Id })),
|
|
16
|
+
expectedHostInstance: OptId,
|
|
17
|
+
sessionPath: OptPath,
|
|
18
|
+
cwd: OptPath,
|
|
19
|
+
provider: OptId,
|
|
20
|
+
modelId: OptId,
|
|
21
|
+
thinkingLevel: Opt(ThinkingLevel),
|
|
22
|
+
permissionPreset: OptId,
|
|
23
|
+
retain_on_disconnect: OptBool,
|
|
24
|
+
kind: Opt(SessionKind),
|
|
25
|
+
context: Opt(SessionContext),
|
|
26
|
+
auto_title: OptBool,
|
|
27
|
+
durableSessionId: OptId,
|
|
28
|
+
promptSurface: Opt(literals("terminal", "app", "chat")),
|
|
29
|
+
browserEngine: Opt(literals("connected", "builtin", "none")),
|
|
30
|
+
retryFallback: Opt(RetryFallback),
|
|
31
|
+
env: Opt(SessionEnv),
|
|
32
|
+
};
|
|
12
33
|
export const HOST_RECORD_SCHEMAS = {
|
|
13
34
|
get_protocol_info: command("get_protocol_info", {
|
|
14
35
|
auth: Opt(Type.Object({ client_nonce: Id })),
|
|
15
36
|
observe: OptBool,
|
|
16
37
|
}),
|
|
17
|
-
open_session: command("open_session",
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
spawnedBy: Opt(Type.Object({ parentSessionId: Id, rootSessionId: Id, taskId: Id })),
|
|
21
|
-
expectedHostInstance: OptId,
|
|
22
|
-
sessionPath: OptPath,
|
|
23
|
-
cwd: OptPath,
|
|
24
|
-
provider: OptId,
|
|
25
|
-
modelId: OptId,
|
|
26
|
-
thinkingLevel: Opt(ThinkingLevel),
|
|
27
|
-
permissionPreset: OptId,
|
|
28
|
-
retain_on_disconnect: OptBool,
|
|
29
|
-
kind: Opt(SessionKind),
|
|
30
|
-
context: Opt(SessionContext),
|
|
31
|
-
auto_title: OptBool,
|
|
32
|
-
durableSessionId: OptId,
|
|
33
|
-
promptSurface: Opt(literals("terminal", "app", "chat")),
|
|
34
|
-
browserEngine: Opt(literals("connected", "builtin", "none")),
|
|
35
|
-
retryFallback: Opt(RetryFallback),
|
|
36
|
-
env: Opt(SessionEnv),
|
|
37
|
-
}),
|
|
38
|
+
open_session: command("open_session", openProperties),
|
|
39
|
+
claim_session: command("claim_session", { ...openProperties, cwd: PathStr }),
|
|
40
|
+
park_session: command("park_session", { cwd: PathStr, kind: Opt(SessionKind), context: Opt(SessionContext) }),
|
|
38
41
|
close_session: command("close_session", { sessionId: Id }),
|
|
39
42
|
move_session: Type.Union([
|
|
40
43
|
move({ op: Type.Literal("prepare"), moveId: Id }),
|
|
@@ -70,6 +70,38 @@ export declare const RPC_INBOUND_SCHEMAS: {
|
|
|
70
70
|
id: import("typebox").TOptional<import("typebox").TString>;
|
|
71
71
|
admissionId: import("typebox").TOptional<import("typebox").TString>;
|
|
72
72
|
type: import("typebox").TLiteral<"open_session">;
|
|
73
|
+
} & {
|
|
74
|
+
retry: import("typebox").TOptional<import("typebox").TLiteral<true>>;
|
|
75
|
+
attended: import("typebox").TOptional<import("typebox").TLiteral<false>>;
|
|
76
|
+
spawnedBy: import("typebox").TOptional<import("typebox").TObject<{
|
|
77
|
+
parentSessionId: import("typebox").TString;
|
|
78
|
+
rootSessionId: import("typebox").TString;
|
|
79
|
+
taskId: import("typebox").TString;
|
|
80
|
+
}>>;
|
|
81
|
+
expectedHostInstance: import("typebox").TOptional<import("typebox").TString>;
|
|
82
|
+
sessionPath: import("typebox").TOptional<import("typebox").TString>;
|
|
83
|
+
cwd: import("typebox").TOptional<import("typebox").TString>;
|
|
84
|
+
provider: import("typebox").TOptional<import("typebox").TString>;
|
|
85
|
+
modelId: import("typebox").TOptional<import("typebox").TString>;
|
|
86
|
+
thinkingLevel: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<string>, ...import("typebox").TLiteral<string>[]]>>;
|
|
87
|
+
permissionPreset: import("typebox").TOptional<import("typebox").TString>;
|
|
88
|
+
retain_on_disconnect: import("typebox").TOptional<import("typebox").TBoolean>;
|
|
89
|
+
kind: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<string>, ...import("typebox").TLiteral<string>[]]>>;
|
|
90
|
+
context: import("typebox").TOptional<import("typebox").TRecord<"^.*$", import("typebox").TString>>;
|
|
91
|
+
auto_title: import("typebox").TOptional<import("typebox").TBoolean>;
|
|
92
|
+
durableSessionId: import("typebox").TOptional<import("typebox").TString>;
|
|
93
|
+
promptSurface: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<string>, ...import("typebox").TLiteral<string>[]]>>;
|
|
94
|
+
browserEngine: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<string>, ...import("typebox").TLiteral<string>[]]>>;
|
|
95
|
+
retryFallback: import("typebox").TOptional<import("typebox").TObject<{
|
|
96
|
+
modelFallback: import("typebox").TBoolean;
|
|
97
|
+
fallbackChains: import("typebox").TRecord<"^.*$", import("typebox").TImmutable<import("typebox").TArray<import("typebox").TString>>>;
|
|
98
|
+
}>>;
|
|
99
|
+
env: import("typebox").TOptional<import("typebox").TRecord<"^.*$", import("typebox").TString>>;
|
|
100
|
+
}>;
|
|
101
|
+
readonly claim_session: import("typebox").TObject<{
|
|
102
|
+
id: import("typebox").TOptional<import("typebox").TString>;
|
|
103
|
+
admissionId: import("typebox").TOptional<import("typebox").TString>;
|
|
104
|
+
type: import("typebox").TLiteral<"claim_session">;
|
|
73
105
|
} & {
|
|
74
106
|
readonly retry: import("typebox").TOptional<import("typebox").TLiteral<true>>;
|
|
75
107
|
readonly attended: import("typebox").TOptional<import("typebox").TLiteral<false>>;
|
|
@@ -80,7 +112,6 @@ export declare const RPC_INBOUND_SCHEMAS: {
|
|
|
80
112
|
}>>;
|
|
81
113
|
readonly expectedHostInstance: import("typebox").TOptional<import("typebox").TString>;
|
|
82
114
|
readonly sessionPath: import("typebox").TOptional<import("typebox").TString>;
|
|
83
|
-
readonly cwd: import("typebox").TOptional<import("typebox").TString>;
|
|
84
115
|
readonly provider: import("typebox").TOptional<import("typebox").TString>;
|
|
85
116
|
readonly modelId: import("typebox").TOptional<import("typebox").TString>;
|
|
86
117
|
readonly thinkingLevel: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<string>, ...import("typebox").TLiteral<string>[]]>>;
|
|
@@ -97,6 +128,16 @@ export declare const RPC_INBOUND_SCHEMAS: {
|
|
|
97
128
|
fallbackChains: import("typebox").TRecord<"^.*$", import("typebox").TImmutable<import("typebox").TArray<import("typebox").TString>>>;
|
|
98
129
|
}>>;
|
|
99
130
|
readonly env: import("typebox").TOptional<import("typebox").TRecord<"^.*$", import("typebox").TString>>;
|
|
131
|
+
readonly cwd: import("typebox").TString;
|
|
132
|
+
}>;
|
|
133
|
+
readonly park_session: import("typebox").TObject<{
|
|
134
|
+
id: import("typebox").TOptional<import("typebox").TString>;
|
|
135
|
+
admissionId: import("typebox").TOptional<import("typebox").TString>;
|
|
136
|
+
type: import("typebox").TLiteral<"park_session">;
|
|
137
|
+
} & {
|
|
138
|
+
readonly cwd: import("typebox").TString;
|
|
139
|
+
readonly kind: import("typebox").TOptional<import("typebox").TUnion<[import("typebox").TLiteral<string>, ...import("typebox").TLiteral<string>[]]>>;
|
|
140
|
+
readonly context: import("typebox").TOptional<import("typebox").TRecord<"^.*$", import("typebox").TString>>;
|
|
100
141
|
}>;
|
|
101
142
|
readonly close_session: import("typebox").TObject<{
|
|
102
143
|
id: import("typebox").TOptional<import("typebox").TString>;
|
|
@@ -37,12 +37,15 @@ function fieldBoundsError(record) {
|
|
|
37
37
|
const payload = rpcCommandPayloadError(record) ?? rpcMessageLengthError(record);
|
|
38
38
|
if (payload !== undefined)
|
|
39
39
|
return payload;
|
|
40
|
-
if (record.type === "open_session" ||
|
|
40
|
+
if (record.type === "open_session" ||
|
|
41
|
+
record.type === "warm" ||
|
|
42
|
+
record.type === "park_session" ||
|
|
43
|
+
record.type === "claim_session") {
|
|
41
44
|
const context = sessionContextError(record.context);
|
|
42
45
|
if (context !== undefined)
|
|
43
46
|
return context;
|
|
44
47
|
}
|
|
45
|
-
if (record.type === "open_session")
|
|
48
|
+
if (record.type === "open_session" || record.type === "claim_session")
|
|
46
49
|
return sessionRetryFallbackError(record.retryFallback) ?? sessionEnvShapeError(record.env);
|
|
47
50
|
if (record.type === "set_retry_fallback")
|
|
48
51
|
return sessionRetryFallbackError(record.retryFallback);
|
|
@@ -163,6 +163,8 @@ export declare const RPC_METHOD_CLASS: {
|
|
|
163
163
|
readonly classify: typeof openSessionClass;
|
|
164
164
|
readonly version: 2;
|
|
165
165
|
};
|
|
166
|
+
readonly claim_session: "device_admin";
|
|
167
|
+
readonly park_session: "device_admin";
|
|
166
168
|
readonly resume_session: "thread";
|
|
167
169
|
readonly fork: "thread";
|
|
168
170
|
readonly clone: "thread";
|
|
@@ -207,6 +207,9 @@ export const RPC_METHOD_CLASS = {
|
|
|
207
207
|
// thread by id; device_admin when the record names a host path, a permission preset or a tool environment
|
|
208
208
|
new_session: { classify: newSessionClass, version: 1 },
|
|
209
209
|
open_session: { classify: openSessionClass, version: 2 },
|
|
210
|
+
// Both commands require a cwd; claim has the same device authority as a cwd-naming open.
|
|
211
|
+
claim_session: "device_admin",
|
|
212
|
+
park_session: "device_admin",
|
|
210
213
|
resume_session: "thread",
|
|
211
214
|
fork: "thread",
|
|
212
215
|
clone: "thread",
|
|
@@ -177,7 +177,7 @@ export function createHostCore(options, writer, capabilities = parseClientCapabi
|
|
|
177
177
|
return;
|
|
178
178
|
}
|
|
179
179
|
const command = parsed;
|
|
180
|
-
const response = await gate.dispatch(command.type === "open_session" &&
|
|
180
|
+
const response = await gate.dispatch((command.type === "open_session" || command.type === "claim_session") &&
|
|
181
181
|
(delegated
|
|
182
182
|
? command.retain_on_disconnect !== true
|
|
183
183
|
: machineOwned && command.retain_on_disconnect === undefined)
|
|
@@ -255,7 +255,20 @@ type RpcCommandBody = RpcAttachmentCommand | (RpcSessionCommand & {
|
|
|
255
255
|
context?: Record<string, string>;
|
|
256
256
|
};
|
|
257
257
|
/** Compact callers may identify their own admission on any correlated session request. */
|
|
258
|
-
export type RpcCommand = RpcCommandBody
|
|
258
|
+
export type RpcCommand = (RpcCommandBody | {
|
|
259
|
+
id?: string;
|
|
260
|
+
/** Build one invisible session for later claim; never an attachment. Requires `park_claim`. */
|
|
261
|
+
type: "park_session";
|
|
262
|
+
cwd: string;
|
|
263
|
+
kind?: SessionKind;
|
|
264
|
+
context?: Record<string, string>;
|
|
265
|
+
} | (Omit<Extract<RpcCommandBody, {
|
|
266
|
+
type: "open_session";
|
|
267
|
+
}>, "type" | "cwd"> & {
|
|
268
|
+
/** Consume one ready park. A miss returns `claimed: false` and never builds a session. */
|
|
269
|
+
type: "claim_session";
|
|
270
|
+
cwd: string;
|
|
271
|
+
})) & {
|
|
259
272
|
readonly admissionId?: string;
|
|
260
273
|
};
|
|
261
274
|
/** Stdin records: session/host commands plus extension-UI replies and progress, and host-request answers. */
|
|
@@ -19,6 +19,27 @@ export interface RpcSessionPresence {
|
|
|
19
19
|
readonly held: number;
|
|
20
20
|
}
|
|
21
21
|
export type RpcHostCommandResponse = {
|
|
22
|
+
id?: string;
|
|
23
|
+
type: "response";
|
|
24
|
+
command: "park_session";
|
|
25
|
+
success: true;
|
|
26
|
+
data: {
|
|
27
|
+
state: "parked" | "already_parked" | "full";
|
|
28
|
+
};
|
|
29
|
+
} | {
|
|
30
|
+
id?: string;
|
|
31
|
+
type: "response";
|
|
32
|
+
command: "claim_session";
|
|
33
|
+
success: true;
|
|
34
|
+
data: {
|
|
35
|
+
claimed: false;
|
|
36
|
+
} | {
|
|
37
|
+
sessionId: string;
|
|
38
|
+
state: RpcSessionState;
|
|
39
|
+
attached?: boolean;
|
|
40
|
+
presence?: RpcSessionPresence;
|
|
41
|
+
};
|
|
42
|
+
} | {
|
|
22
43
|
id?: string;
|
|
23
44
|
type: "response";
|
|
24
45
|
command: "list_hands";
|
|
@@ -4,6 +4,7 @@ import type { RpcSessionEntry } from "./session-registry.ts";
|
|
|
4
4
|
/** A session-owned adapter around the classic command and extension-UI wiring. */
|
|
5
5
|
export interface RpcSessionBinding {
|
|
6
6
|
handle(command: object): Promise<void>;
|
|
7
|
+
setClientCapabilities?(capabilities: readonly string[]): void;
|
|
7
8
|
cancelPendingExtensionUiRequests?(): void;
|
|
8
9
|
/** A connection of the shared host left this session (`close_session`, a dropped or detached connection). */
|
|
9
10
|
connectionDetached?(connectionId: string): void;
|
|
@@ -40,6 +40,8 @@ export async function createRpcSessionBinding(sessionId, entry, writer, requestC
|
|
|
40
40
|
return carrier && record && firstOrdinal !== undefined ? { record, carrier, ordinal, firstOrdinal } : undefined;
|
|
41
41
|
};
|
|
42
42
|
const enqueueRecord = (record) => {
|
|
43
|
+
if (entry.speculative)
|
|
44
|
+
return;
|
|
43
45
|
toolSpans.observe(record);
|
|
44
46
|
const canonical = pairing?.take(record);
|
|
45
47
|
const refused = pairing?.refused(record) === true;
|
|
@@ -103,6 +105,8 @@ export async function createRpcSessionBinding(sessionId, entry, writer, requestC
|
|
|
103
105
|
writeInScope(record);
|
|
104
106
|
};
|
|
105
107
|
const writeStructuredInScope = bindToProviderScope((record) => {
|
|
108
|
+
if (entry.speculative)
|
|
109
|
+
return;
|
|
106
110
|
writer.structuredLanes.accept(sessionId, record);
|
|
107
111
|
const control = pairing?.structured(record);
|
|
108
112
|
if (control && lane)
|
|
@@ -151,7 +155,7 @@ export async function createRpcSessionBinding(sessionId, entry, writer, requestC
|
|
|
151
155
|
rpcOwnersByConnection.clear();
|
|
152
156
|
},
|
|
153
157
|
publishLoadedSurfacesChanged: (previous, digest) => {
|
|
154
|
-
if (entry.scope.state !== "closed")
|
|
158
|
+
if (!entry.speculative && entry.scope.state !== "closed")
|
|
155
159
|
writer.publishLoadedSurfacesChanged(sessionId, previous, digest);
|
|
156
160
|
},
|
|
157
161
|
...options,
|
|
@@ -171,6 +175,7 @@ export async function createRpcSessionBinding(sessionId, entry, writer, requestC
|
|
|
171
175
|
});
|
|
172
176
|
await handler.ready;
|
|
173
177
|
return {
|
|
178
|
+
setClientCapabilities: (capabilities) => runWithProviderScope(entry.scope, () => handler.setClientCapabilities(capabilities)),
|
|
174
179
|
handle: (command) => {
|
|
175
180
|
if (writer.handleMcpForm(command))
|
|
176
181
|
return Promise.resolve();
|
|
@@ -7,6 +7,8 @@ export declare class SessionConnectionContext {
|
|
|
7
7
|
private readonly delegationOf;
|
|
8
8
|
constructor(ownerOf: (id: string) => ExtensionTransportOwner | undefined, delegationOf?: (id: string) => DelegationDescriptor | undefined);
|
|
9
9
|
run<T>(id: string, task: () => T): T;
|
|
10
|
+
/** Host-owned background construction has no requesting transport or inherited delegation. */
|
|
11
|
+
outside<T>(task: () => T): T;
|
|
10
12
|
/** The delegation the running line captured at its start, or undefined for a connection that never declared. */
|
|
11
13
|
get delegation(): DelegationDescriptor | undefined;
|
|
12
14
|
/**
|
|
@@ -10,6 +10,10 @@ export class SessionConnectionContext {
|
|
|
10
10
|
const delegation = this.delegationOf(id);
|
|
11
11
|
return this.storage.run({ id, owner: this.ownerOf(id), ...(delegation === undefined ? {} : { delegation }) }, task);
|
|
12
12
|
}
|
|
13
|
+
/** Host-owned background construction has no requesting transport or inherited delegation. */
|
|
14
|
+
outside(task) {
|
|
15
|
+
return this.storage.exit(task);
|
|
16
|
+
}
|
|
13
17
|
/** The delegation the running line captured at its start, or undefined for a connection that never declared. */
|
|
14
18
|
get delegation() {
|
|
15
19
|
return this.storage.getStore()?.delegation;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { SessionPathReservations } from "./host-reservations.ts";
|
|
2
2
|
import { type OpenRpcSession, type RpcSessionEntry, type RpcSessionLaunchProfile, type RpcSessionOpenOptions } from "./session-registry-types.ts";
|
|
3
3
|
export declare function attachToOpenSession(entries: ReadonlyMap<string, RpcSessionEntry>, sessionPath: string, profile: RpcSessionLaunchProfile, options: RpcSessionOpenOptions | undefined, pathReservations: SessionPathReservations | undefined, now: number): Promise<OpenRpcSession>;
|
|
4
|
+
/** The live launch choices shared by attach-on-open and claiming an already-built session. */
|
|
5
|
+
export declare function applyAttachedProfile(entry: RpcSessionEntry, profile: RpcSessionLaunchProfile): void;
|
|
4
6
|
//# sourceMappingURL=session-registry-attach.d.ts.map
|
|
@@ -102,6 +102,21 @@ export async function attachToOpenSession(entries, sessionPath, profile, options
|
|
|
102
102
|
entry.retainOnDisconnect = true;
|
|
103
103
|
if (!entry.durableSessionId)
|
|
104
104
|
throw new RpcSessionRegistryError("session_path_in_use");
|
|
105
|
+
applyAttachedProfile(entry, profile);
|
|
106
|
+
entry.lastCommandAt = now;
|
|
107
|
+
if (wasParked) {
|
|
108
|
+
entry.lifecycleMutex = entry.lifecycleMutex.then(() => entry.runtime?.emitAttachmentEvent("session_resumed"));
|
|
109
|
+
await entry.lifecycleMutex;
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
sessionId: handle,
|
|
113
|
+
durableSessionId: entry.durableSessionId,
|
|
114
|
+
sessionPath: entry.sessionPath,
|
|
115
|
+
attached: true,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/** The live launch choices shared by attach-on-open and claiming an already-built session. */
|
|
119
|
+
export function applyAttachedProfile(entry, profile) {
|
|
105
120
|
// The surface, browser engine and permission preset follow the client: an attach that names one
|
|
106
121
|
// moves the live session to it; an attach without one keeps what the session has.
|
|
107
122
|
if (profile.promptSurface !== undefined && profile.promptSurface !== entry.profile.promptSurface) {
|
|
@@ -121,16 +136,5 @@ export async function attachToOpenSession(entries, sessionPath, profile, options
|
|
|
121
136
|
entry.profile = frozenProfile(withClientPreset(entry.profile, namedPreset));
|
|
122
137
|
entry.runtime?.setPermissionPreset(namedPreset);
|
|
123
138
|
}
|
|
124
|
-
entry.lastCommandAt = now;
|
|
125
|
-
if (wasParked) {
|
|
126
|
-
entry.lifecycleMutex = entry.lifecycleMutex.then(() => entry.runtime?.emitAttachmentEvent("session_resumed"));
|
|
127
|
-
await entry.lifecycleMutex;
|
|
128
|
-
}
|
|
129
|
-
return {
|
|
130
|
-
sessionId: handle,
|
|
131
|
-
durableSessionId: entry.durableSessionId,
|
|
132
|
-
sessionPath: entry.sessionPath,
|
|
133
|
-
attached: true,
|
|
134
|
-
};
|
|
135
139
|
}
|
|
136
140
|
//# sourceMappingURL=session-registry-attach.js.map
|
|
@@ -70,6 +70,8 @@ export async function settlePendingClaim(pending, sessionPath, graceMs) {
|
|
|
70
70
|
/** Reconcile path and durable identity after runtime replacement. */
|
|
71
71
|
export function syncRuntimeMetadata(entries, reservations, pathReservations) {
|
|
72
72
|
for (const entry of entries) {
|
|
73
|
+
if (entry.speculative)
|
|
74
|
+
continue;
|
|
73
75
|
const manager = entry.runtime?.session.sessionManager;
|
|
74
76
|
if (!manager)
|
|
75
77
|
continue;
|
|
@@ -28,7 +28,7 @@ export async function openRegistrySession(host, requested, options) {
|
|
|
28
28
|
const requestedDurableId = profile.durableSessionId;
|
|
29
29
|
if (requestedDurableId !== undefined) {
|
|
30
30
|
for (const entry of host.entries.values()) {
|
|
31
|
-
if (entry.state === "closed")
|
|
31
|
+
if (entry.state === "closed" || entry.speculative)
|
|
32
32
|
continue;
|
|
33
33
|
if (entry.durableSessionId !== requestedDurableId)
|
|
34
34
|
continue;
|
|
@@ -101,7 +101,8 @@ export async function openRegistrySession(host, requested, options) {
|
|
|
101
101
|
const { entry, storedProfile, runtimeProfile, sessionStartEvent, settleOpen } = opening;
|
|
102
102
|
host.entries.set(handle, entry);
|
|
103
103
|
claimSettled?.();
|
|
104
|
-
|
|
104
|
+
if (!entry.speculative)
|
|
105
|
+
host.options.onSizeChange?.([...host.entries.values()].filter((item) => !item.speculative).length);
|
|
105
106
|
const timer = startOpenTimer(`registry-open:${handle}`);
|
|
106
107
|
try {
|
|
107
108
|
const newSessionOptions = {
|
|
@@ -112,7 +113,9 @@ export async function openRegistrySession(host, requested, options) {
|
|
|
112
113
|
? host.options.sessionFileLoad === "blocking"
|
|
113
114
|
? SessionManager.open(sessionPath, undefined, storedProfile.cwd, newSessionOptions)
|
|
114
115
|
: await SessionManager.openAsync(sessionPath, undefined, storedProfile.cwd, newSessionOptions, host.options.createSlicer?.())
|
|
115
|
-
:
|
|
116
|
+
: options?.speculative
|
|
117
|
+
? SessionManager.inMemory(storedProfile.cwd, newSessionOptions)
|
|
118
|
+
: SessionManager.create(storedProfile.cwd, undefined, newSessionOptions);
|
|
116
119
|
timer.mark("session_manager");
|
|
117
120
|
entry.runtime = await runWithProviderScope(entry.scope, () => createAgentSessionRuntime(host.options.createRuntime, {
|
|
118
121
|
cwd: manager.getCwd(),
|
|
@@ -125,19 +128,21 @@ export async function openRegistrySession(host, requested, options) {
|
|
|
125
128
|
assertRequestedModelResolved(runtimeProfile, entry.runtime.diagnostics);
|
|
126
129
|
ClientAdmissions.sealQueuedOnReplace(entry.runtime);
|
|
127
130
|
EffectLedger.sealOnReplace(entry.runtime);
|
|
128
|
-
|
|
131
|
+
const durableSessionId = manager.getSessionId();
|
|
132
|
+
if (!entry.speculative)
|
|
133
|
+
entry.durableSessionId = durableSessionId;
|
|
129
134
|
entry.sessionPath ??= manager.getSessionFile();
|
|
130
135
|
// The accepted baseline precedes the open, so router admission and its reply come after it (R3.C1.1).
|
|
131
136
|
// Gated like a worker incarnation's lane: with the flag off no session owns one (RULING-112).
|
|
132
137
|
if (host.options.workerIsolate !== true && structuredLaneEnabled()) {
|
|
133
138
|
entry.acceptance = new SessionAcceptanceLane(undefined, undefined, host.options.agentDir);
|
|
134
|
-
await entry.acceptance.generations.bind(
|
|
139
|
+
await entry.acceptance.generations.bind(durableSessionId);
|
|
135
140
|
entry.acceptance.bootstrap({ transcript: manager.beginTranscriptJournal() });
|
|
136
141
|
setSessionRecoveryEpoch(entry.runtime.session, entry.acceptance.recoveryEpoch);
|
|
137
142
|
}
|
|
138
143
|
entry.state = "open";
|
|
139
144
|
settleOpen.resolve();
|
|
140
|
-
return { sessionId: handle, durableSessionId
|
|
145
|
+
return { sessionId: handle, durableSessionId, sessionPath: entry.sessionPath };
|
|
141
146
|
}
|
|
142
147
|
catch (error) {
|
|
143
148
|
// Runtime construction may have started extensions, watchers, and provider
|
|
@@ -156,7 +161,8 @@ export async function openRegistrySession(host, requested, options) {
|
|
|
156
161
|
}
|
|
157
162
|
finally {
|
|
158
163
|
host.entries.delete(handle);
|
|
159
|
-
|
|
164
|
+
if (!entry.speculative)
|
|
165
|
+
host.options.onSizeChange?.([...host.entries.values()].filter((item) => !item.speculative).length);
|
|
160
166
|
if (sessionPath)
|
|
161
167
|
await releaseWithinGrace(host.teardownHost, handle, sessionPath);
|
|
162
168
|
}
|
|
@@ -38,13 +38,14 @@ export function buildOpeningEntry(input) {
|
|
|
38
38
|
openCompletion.catch(() => { });
|
|
39
39
|
const entry = {
|
|
40
40
|
state: "opening",
|
|
41
|
+
...(options?.speculative ? { speculative: true } : {}),
|
|
41
42
|
scope: new ProviderScope(),
|
|
42
43
|
profile: storedProfile,
|
|
43
44
|
...sessionIdentity(storedProfile),
|
|
44
45
|
sessionPath,
|
|
45
46
|
reservationKey: sessionPath,
|
|
46
47
|
cwd: storedProfile.cwd,
|
|
47
|
-
attachments: 1,
|
|
48
|
+
attachments: options?.speculative ? 0 : 1,
|
|
48
49
|
retainOnDisconnect: options?.retainOnDisconnect === true,
|
|
49
50
|
lastCommandAt: input.now,
|
|
50
51
|
lifecycleMutex: Promise.resolve(),
|