@celestea/studio 2.7.1
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/LICENSE +21 -0
- package/dist/app.d.ts +111 -0
- package/dist/app.js +219 -0
- package/dist/auth/api-token.d.ts +76 -0
- package/dist/auth/api-token.js +172 -0
- package/dist/auth/htpasswd.d.ts +22 -0
- package/dist/auth/htpasswd.js +61 -0
- package/dist/auth/index.d.ts +14 -0
- package/dist/auth/index.js +14 -0
- package/dist/auth/page.d.ts +19 -0
- package/dist/auth/page.js +73 -0
- package/dist/auth/rate-limit.d.ts +27 -0
- package/dist/auth/rate-limit.js +47 -0
- package/dist/auth/token.d.ts +49 -0
- package/dist/auth/token.js +112 -0
- package/dist/config.d.ts +78 -0
- package/dist/config.js +72 -0
- package/dist/deployment.d.ts +92 -0
- package/dist/deployment.js +154 -0
- package/dist/fake-runtime-adapter.d.ts +37 -0
- package/dist/fake-runtime-adapter.js +276 -0
- package/dist/handlers/auth.d.ts +24 -0
- package/dist/handlers/auth.js +155 -0
- package/dist/handlers/common.d.ts +76 -0
- package/dist/handlers/common.js +127 -0
- package/dist/handlers/config-shape.d.ts +76 -0
- package/dist/handlers/config-shape.js +227 -0
- package/dist/handlers/config.d.ts +26 -0
- package/dist/handlers/config.js +161 -0
- package/dist/handlers/context-shape.d.ts +33 -0
- package/dist/handlers/context-shape.js +59 -0
- package/dist/handlers/dialog.d.ts +22 -0
- package/dist/handlers/dialog.js +292 -0
- package/dist/handlers/exec.d.ts +22 -0
- package/dist/handlers/exec.js +131 -0
- package/dist/handlers/fs-read.d.ts +63 -0
- package/dist/handlers/fs-read.js +159 -0
- package/dist/handlers/fs.d.ts +57 -0
- package/dist/handlers/fs.js +153 -0
- package/dist/handlers/grants-shape.d.ts +45 -0
- package/dist/handlers/grants-shape.js +130 -0
- package/dist/handlers/grants.d.ts +21 -0
- package/dist/handlers/grants.js +267 -0
- package/dist/handlers/health.d.ts +37 -0
- package/dist/handlers/health.js +139 -0
- package/dist/handlers/index.d.ts +40 -0
- package/dist/handlers/index.js +85 -0
- package/dist/handlers/permissions.d.ts +9 -0
- package/dist/handlers/permissions.js +183 -0
- package/dist/handlers/plugins.d.ts +18 -0
- package/dist/handlers/plugins.js +23 -0
- package/dist/handlers/prompts.d.ts +12 -0
- package/dist/handlers/prompts.js +120 -0
- package/dist/handlers/providers.d.ts +12 -0
- package/dist/handlers/providers.js +178 -0
- package/dist/handlers/questions.d.ts +23 -0
- package/dist/handlers/questions.js +115 -0
- package/dist/handlers/session-model.d.ts +38 -0
- package/dist/handlers/session-model.js +105 -0
- package/dist/handlers/session-move.d.ts +22 -0
- package/dist/handlers/session-move.js +188 -0
- package/dist/handlers/session-tools.d.ts +19 -0
- package/dist/handlers/session-tools.js +75 -0
- package/dist/handlers/sessions.d.ts +32 -0
- package/dist/handlers/sessions.js +242 -0
- package/dist/handlers/usage.d.ts +26 -0
- package/dist/handlers/usage.js +77 -0
- package/dist/handlers/worker.d.ts +12 -0
- package/dist/handlers/worker.js +82 -0
- package/dist/handlers/workspaces.d.ts +12 -0
- package/dist/handlers/workspaces.js +88 -0
- package/dist/harness.test-util.d.ts +101 -0
- package/dist/harness.test-util.js +176 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +33 -0
- package/dist/main.d.ts +13 -0
- package/dist/main.js +35 -0
- package/dist/plugins.d.ts +105 -0
- package/dist/plugins.js +97 -0
- package/dist/question-registry.d.ts +124 -0
- package/dist/question-registry.js +212 -0
- package/dist/question-rows.d.ts +32 -0
- package/dist/question-rows.js +43 -0
- package/dist/replay/compare.d.ts +47 -0
- package/dist/replay/compare.js +89 -0
- package/dist/replay/e2e-replay.d.ts +46 -0
- package/dist/replay/e2e-replay.js +91 -0
- package/dist/replay/expect-compact.d.ts +31 -0
- package/dist/replay/expect-compact.js +92 -0
- package/dist/replay/fixtures.d.ts +52 -0
- package/dist/replay/fixtures.js +53 -0
- package/dist/replay/host.d.ts +32 -0
- package/dist/replay/host.js +62 -0
- package/dist/replay/index.d.ts +16 -0
- package/dist/replay/index.js +16 -0
- package/dist/replay/probes.d.ts +55 -0
- package/dist/replay/probes.js +225 -0
- package/dist/replay/report.d.ts +13 -0
- package/dist/replay/report.js +103 -0
- package/dist/replay/session-e2e.d.ts +44 -0
- package/dist/replay/session-e2e.js +91 -0
- package/dist/routes.d.ts +47 -0
- package/dist/routes.js +59 -0
- package/dist/runtime/attachments-llm.d.ts +13 -0
- package/dist/runtime/attachments-llm.js +37 -0
- package/dist/runtime/boot-recovery.d.ts +32 -0
- package/dist/runtime/boot-recovery.js +71 -0
- package/dist/runtime/context-snapshot.d.ts +59 -0
- package/dist/runtime/context-snapshot.js +113 -0
- package/dist/runtime/engine-grants.d.ts +121 -0
- package/dist/runtime/engine-grants.js +338 -0
- package/dist/runtime/engine-permissions.d.ts +29 -0
- package/dist/runtime/engine-permissions.js +92 -0
- package/dist/runtime/engine-plugins.d.ts +189 -0
- package/dist/runtime/engine-plugins.js +292 -0
- package/dist/runtime/engine-profile.d.ts +28 -0
- package/dist/runtime/engine-profile.js +88 -0
- package/dist/runtime/engine-session.d.ts +9 -0
- package/dist/runtime/engine-session.js +9 -0
- package/dist/runtime/fallback-contract.d.ts +32 -0
- package/dist/runtime/fallback-contract.js +10 -0
- package/dist/runtime/fallback-host.d.ts +134 -0
- package/dist/runtime/fallback-host.js +299 -0
- package/dist/runtime/host-autowake.d.ts +79 -0
- package/dist/runtime/host-autowake.js +86 -0
- package/dist/runtime/image-downgrade.d.ts +49 -0
- package/dist/runtime/image-downgrade.js +90 -0
- package/dist/runtime/inbox-message.d.ts +11 -0
- package/dist/runtime/inbox-message.js +20 -0
- package/dist/runtime/index.d.ts +17 -0
- package/dist/runtime/index.js +17 -0
- package/dist/runtime/ledger-view.d.ts +40 -0
- package/dist/runtime/ledger-view.js +56 -0
- package/dist/runtime/llm-assembly.d.ts +66 -0
- package/dist/runtime/llm-assembly.js +114 -0
- package/dist/runtime/offline-llm.d.ts +71 -0
- package/dist/runtime/offline-llm.js +132 -0
- package/dist/runtime/provider-target.d.ts +10 -0
- package/dist/runtime/provider-target.js +10 -0
- package/dist/runtime/question-host.d.ts +33 -0
- package/dist/runtime/question-host.js +41 -0
- package/dist/runtime/question-view.d.ts +48 -0
- package/dist/runtime/question-view.js +76 -0
- package/dist/runtime/real-runtime-adapter.d.ts +95 -0
- package/dist/runtime/real-runtime-adapter.js +553 -0
- package/dist/runtime/recovery-audit.d.ts +81 -0
- package/dist/runtime/recovery-audit.js +95 -0
- package/dist/runtime/recovery-view.d.ts +36 -0
- package/dist/runtime/recovery-view.js +63 -0
- package/dist/runtime/session-compose.d.ts +235 -0
- package/dist/runtime/session-compose.js +350 -0
- package/dist/runtime/session-grants.d.ts +36 -0
- package/dist/runtime/session-grants.js +69 -0
- package/dist/runtime/session-lifecycle.d.ts +40 -0
- package/dist/runtime/session-lifecycle.js +86 -0
- package/dist/runtime/session-publisher.d.ts +40 -0
- package/dist/runtime/session-publisher.js +48 -0
- package/dist/runtime/session-release.d.ts +53 -0
- package/dist/runtime/session-release.js +80 -0
- package/dist/runtime/test-util.d.ts +78 -0
- package/dist/runtime/test-util.js +162 -0
- package/dist/runtime/watchdog-view.d.ts +33 -0
- package/dist/runtime/watchdog-view.js +45 -0
- package/dist/runtime/worker-bridge.d.ts +76 -0
- package/dist/runtime/worker-bridge.js +156 -0
- package/dist/runtime/worker-live.d.ts +22 -0
- package/dist/runtime/worker-live.js +25 -0
- package/dist/runtime/worker-recovery.d.ts +35 -0
- package/dist/runtime/worker-recovery.js +64 -0
- package/dist/runtime/worker-table.d.ts +58 -0
- package/dist/runtime/worker-table.js +70 -0
- package/dist/runtime-adapter.d.ts +421 -0
- package/dist/runtime-adapter.js +76 -0
- package/dist/serial-queue.d.ts +16 -0
- package/dist/serial-queue.js +22 -0
- package/dist/server.d.ts +44 -0
- package/dist/server.js +119 -0
- package/dist/settings.d.ts +23 -0
- package/dist/settings.js +31 -0
- package/dist/sse.d.ts +67 -0
- package/dist/sse.js +186 -0
- package/dist/static.d.ts +20 -0
- package/dist/static.js +120 -0
- package/dist/store/builtin-sections.d.ts +52 -0
- package/dist/store/builtin-sections.js +118 -0
- package/dist/store/celestea-home.d.ts +12 -0
- package/dist/store/celestea-home.js +11 -0
- package/dist/store/fs-json.d.ts +51 -0
- package/dist/store/fs-json.js +119 -0
- package/dist/store/grants-audit.d.ts +84 -0
- package/dist/store/grants-audit.js +109 -0
- package/dist/store/grants-service.d.ts +44 -0
- package/dist/store/grants-service.js +41 -0
- package/dist/store/grants-tokens.d.ts +62 -0
- package/dist/store/grants-tokens.js +105 -0
- package/dist/store/grants.d.ts +113 -0
- package/dist/store/grants.js +238 -0
- package/dist/store/index.d.ts +36 -0
- package/dist/store/index.js +36 -0
- package/dist/store/mode.d.ts +35 -0
- package/dist/store/mode.js +41 -0
- package/dist/store/permissions.d.ts +61 -0
- package/dist/store/permissions.js +139 -0
- package/dist/store/prompts-compose.d.ts +56 -0
- package/dist/store/prompts-compose.js +62 -0
- package/dist/store/prompts-template.d.ts +26 -0
- package/dist/store/prompts-template.js +99 -0
- package/dist/store/prompts.d.ts +142 -0
- package/dist/store/prompts.js +273 -0
- package/dist/store/provider-probe.d.ts +81 -0
- package/dist/store/provider-probe.js +154 -0
- package/dist/store/providers.d.ts +97 -0
- package/dist/store/providers.js +219 -0
- package/dist/store/result.d.ts +29 -0
- package/dist/store/result.js +30 -0
- package/dist/store/session-id.d.ts +119 -0
- package/dist/store/session-id.js +220 -0
- package/dist/store/session-meta.d.ts +41 -0
- package/dist/store/session-meta.js +66 -0
- package/dist/store/session-ops.d.ts +109 -0
- package/dist/store/session-ops.js +264 -0
- package/dist/store/session-tools.d.ts +42 -0
- package/dist/store/session-tools.js +69 -0
- package/dist/store/sessions.d.ts +162 -0
- package/dist/store/sessions.js +307 -0
- package/dist/store/validate.d.ts +13 -0
- package/dist/store/validate.js +38 -0
- package/dist/store/workspaces.d.ts +67 -0
- package/dist/store/workspaces.js +228 -0
- package/dist/user-questions.d.ts +58 -0
- package/dist/user-questions.js +157 -0
- package/dist/version.d.ts +14 -0
- package/dist/version.js +52 -0
- package/package.json +38 -0
- package/webdist/assets/index-Bngs7gUm.js +98 -0
- package/webdist/assets/index-Bu6ci_rN.css +1 -0
- package/webdist/assets/katex-CCbS2qAY.js +257 -0
- package/webdist/assets/mhchem-CnUN8HwY.js +1 -0
- package/webdist/build-meta.json +7 -0
- package/webdist/index.html +261 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SSE publishers the host raises on behalf of ONE session (W515 §2/§4).
|
|
3
|
+
*
|
|
4
|
+
* Two concerns that look alike and are kept together for that reason:
|
|
5
|
+
* - an injected message's placement change (`status` frames: `queued` /
|
|
6
|
+
* `steering` on acceptance, `context` when a boundary consumes it);
|
|
7
|
+
* - a parked user question (`question`, W783).
|
|
8
|
+
*
|
|
9
|
+
* Both are "the adapter knows the session id and the bus; nobody else does", so
|
|
10
|
+
* both are built from the same two callbacks and neither needs the engine.
|
|
11
|
+
*
|
|
12
|
+
* NOTE (pure move, W783): the injection publisher below is W515 behaviour that
|
|
13
|
+
* used to live inline in `real-runtime-adapter.ts`. Nothing about what it emits
|
|
14
|
+
* changed — only where it is written down.
|
|
15
|
+
*/
|
|
16
|
+
import { createSessionInbox } from "@celestea/runtime";
|
|
17
|
+
import { inboxMessageOf } from "./inbox-message.js";
|
|
18
|
+
/**
|
|
19
|
+
* The injection hooks of one session: the ACCEPT side is observed when the inbox
|
|
20
|
+
* queues a message, and the CONSUME side only through `onInjected`, which knows
|
|
21
|
+
* WHICH boundary drained it (a mailbox receipt never enters the inbox, so it
|
|
22
|
+
* cannot be reported here).
|
|
23
|
+
*/
|
|
24
|
+
export function injectionHooksOf(sessionId, deps) {
|
|
25
|
+
const publish = (placement, message, boundary) => {
|
|
26
|
+
deps.emitStatus(sessionId, {
|
|
27
|
+
phase: "progress",
|
|
28
|
+
placement,
|
|
29
|
+
...(boundary === undefined ? {} : { boundary }),
|
|
30
|
+
message: {
|
|
31
|
+
id: message.id,
|
|
32
|
+
kind: message.kind,
|
|
33
|
+
from: message.from,
|
|
34
|
+
lane: message.lane,
|
|
35
|
+
source: message.source,
|
|
36
|
+
summary: message.source.summary ?? message.text.slice(0, 120),
|
|
37
|
+
},
|
|
38
|
+
statusline: {},
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
inbox: createSessionInbox(deps.now, { onQueued: (message, placement) => publish(placement, message) }),
|
|
43
|
+
onInjected: (messages, boundary) => {
|
|
44
|
+
for (const message of messages)
|
|
45
|
+
publish("context", inboxMessageOf(message), boundary);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W794 — releasing the engine generation of a session whose DIRECTORY is going
|
|
3
|
+
* away (deleted into `<ws>/.celestea-trash/`, or archived).
|
|
4
|
+
*
|
|
5
|
+
* Extracted from `real-runtime-adapter.ts` for the same reason
|
|
6
|
+
* `session-lifecycle.ts` was (W787): the adapter is the HTTP seam and sits on the
|
|
7
|
+
* §4.1 file budget, while "cut the response in flight, then hand the generation
|
|
8
|
+
* back" is about a session's lifetime, not about the seam.
|
|
9
|
+
*
|
|
10
|
+
* 裁决 background: the active marker is a state label, not a protection, so a
|
|
11
|
+
* session can be removed while it is streaming an answer. Deleting it must (a)
|
|
12
|
+
* SUCCEED and (b) leave nothing of it running — no model response mid-flight, no
|
|
13
|
+
* instance quietly writing into a directory that moved, no auto-wake loop probing
|
|
14
|
+
* for a generation that can never come back.
|
|
15
|
+
*/
|
|
16
|
+
import type { SessionRuntimeRegistry } from "@celestea/runtime";
|
|
17
|
+
/**
|
|
18
|
+
* Default budget of the settle wait below. Generous enough for a cooperative
|
|
19
|
+
* abort of a real step; short enough that a wedged turn cannot hold a delete
|
|
20
|
+
* request open. `CELESTEA_RELEASE_SETTLE_MS` overrides it.
|
|
21
|
+
*/
|
|
22
|
+
export declare const RELEASE_SETTLE_MS = 1500;
|
|
23
|
+
/** The effective release-settle budget of one process. */
|
|
24
|
+
export declare function releaseSettleMs(env: NodeJS.ProcessEnv): number;
|
|
25
|
+
export interface SessionReleaseDeps {
|
|
26
|
+
registry: SessionRuntimeRegistry;
|
|
27
|
+
/** The adapter's cooperative abort — the SAME one `POST /api/cancel` sends. */
|
|
28
|
+
cancel: (session: string) => boolean;
|
|
29
|
+
/** W769: unpark the session's auto-wake loop for good. */
|
|
30
|
+
forget: (session: string) => Promise<void>;
|
|
31
|
+
/** How long to wait for the aborted turn to settle. */
|
|
32
|
+
settleMs: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Cut the session's in-flight model response and drop ITS generation.
|
|
36
|
+
*
|
|
37
|
+
* Three steps, in this order and for these reasons:
|
|
38
|
+
* 1. `cancel(session)` — the cooperative abort, because it is the only path
|
|
39
|
+
* that unwinds a step cleanly (the loop writes its own `turn_end:
|
|
40
|
+
* cancelled`); a removal may not leave a response streaming into a
|
|
41
|
+
* directory that is leaving;
|
|
42
|
+
* 2. wait (bounded by `settleMs`) for that turn to settle, so the log write
|
|
43
|
+
* lands and no writer survives when the directory moves;
|
|
44
|
+
* 3. `registry.release` — detach + dispose THIS instance through the very
|
|
45
|
+
* disposer an evict/rebuild uses. The session's next activate/turn composes
|
|
46
|
+
* a fresh generation on demand; no other session and no process-wide
|
|
47
|
+
* generation is touched.
|
|
48
|
+
*
|
|
49
|
+
* `false` = this session had no live instance (an unknown id, or one that was
|
|
50
|
+
* never used) — nothing to cut, which is what makes the call safe for every id
|
|
51
|
+
* of a batch. `null` (the detached default generation) is never released.
|
|
52
|
+
*/
|
|
53
|
+
export declare function releaseSessionOf(deps: SessionReleaseDeps, session: string | null): Promise<boolean>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W794 — releasing the engine generation of a session whose DIRECTORY is going
|
|
3
|
+
* away (deleted into `<ws>/.celestea-trash/`, or archived).
|
|
4
|
+
*
|
|
5
|
+
* Extracted from `real-runtime-adapter.ts` for the same reason
|
|
6
|
+
* `session-lifecycle.ts` was (W787): the adapter is the HTTP seam and sits on the
|
|
7
|
+
* §4.1 file budget, while "cut the response in flight, then hand the generation
|
|
8
|
+
* back" is about a session's lifetime, not about the seam.
|
|
9
|
+
*
|
|
10
|
+
* 裁决 background: the active marker is a state label, not a protection, so a
|
|
11
|
+
* session can be removed while it is streaming an answer. Deleting it must (a)
|
|
12
|
+
* SUCCEED and (b) leave nothing of it running — no model response mid-flight, no
|
|
13
|
+
* instance quietly writing into a directory that moved, no auto-wake loop probing
|
|
14
|
+
* for a generation that can never come back.
|
|
15
|
+
*/
|
|
16
|
+
import { limitFromEnv } from "./session-compose.js";
|
|
17
|
+
/**
|
|
18
|
+
* Default budget of the settle wait below. Generous enough for a cooperative
|
|
19
|
+
* abort of a real step; short enough that a wedged turn cannot hold a delete
|
|
20
|
+
* request open. `CELESTEA_RELEASE_SETTLE_MS` overrides it.
|
|
21
|
+
*/
|
|
22
|
+
export const RELEASE_SETTLE_MS = 1_500;
|
|
23
|
+
/** Poll granularity of that wait. */
|
|
24
|
+
const SETTLE_POLL_MS = 5;
|
|
25
|
+
/** The effective release-settle budget of one process. */
|
|
26
|
+
export function releaseSettleMs(env) {
|
|
27
|
+
return limitFromEnv(env, "CELESTEA_RELEASE_SETTLE_MS", RELEASE_SETTLE_MS);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Cut the session's in-flight model response and drop ITS generation.
|
|
31
|
+
*
|
|
32
|
+
* Three steps, in this order and for these reasons:
|
|
33
|
+
* 1. `cancel(session)` — the cooperative abort, because it is the only path
|
|
34
|
+
* that unwinds a step cleanly (the loop writes its own `turn_end:
|
|
35
|
+
* cancelled`); a removal may not leave a response streaming into a
|
|
36
|
+
* directory that is leaving;
|
|
37
|
+
* 2. wait (bounded by `settleMs`) for that turn to settle, so the log write
|
|
38
|
+
* lands and no writer survives when the directory moves;
|
|
39
|
+
* 3. `registry.release` — detach + dispose THIS instance through the very
|
|
40
|
+
* disposer an evict/rebuild uses. The session's next activate/turn composes
|
|
41
|
+
* a fresh generation on demand; no other session and no process-wide
|
|
42
|
+
* generation is touched.
|
|
43
|
+
*
|
|
44
|
+
* `false` = this session had no live instance (an unknown id, or one that was
|
|
45
|
+
* never used) — nothing to cut, which is what makes the call safe for every id
|
|
46
|
+
* of a batch. `null` (the detached default generation) is never released.
|
|
47
|
+
*/
|
|
48
|
+
export async function releaseSessionOf(deps, session) {
|
|
49
|
+
if (session === null)
|
|
50
|
+
return false;
|
|
51
|
+
const entry = deps.registry.peek(session);
|
|
52
|
+
if (entry === null) {
|
|
53
|
+
// W833 (R3 B7 / W816 F2): "no live instance" does NOT mean "no loop". An
|
|
54
|
+
// idle-evicted session keeps its autowake loop parked against a generation
|
|
55
|
+
// that can never return; deleting/archiving it must forget the loop here or
|
|
56
|
+
// it logs "no live generation to bind" and burns a timer forever.
|
|
57
|
+
await deps.forget(session);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (entry.inFlight) {
|
|
61
|
+
deps.cancel(session);
|
|
62
|
+
await awaitSettled(entry, deps.settleMs);
|
|
63
|
+
}
|
|
64
|
+
await deps.forget(session);
|
|
65
|
+
return deps.registry.release(session);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Wait — with a REAL clock, whatever `opts.now` says — for an aborted turn to
|
|
69
|
+
* reach its terminal state. Polling rather than a callback keeps the wait
|
|
70
|
+
* independent of which teardown path ends the turn (settled turn, failed turn,
|
|
71
|
+
* the `drive()` tail), which is exactly the set we cannot enumerate from here. A
|
|
72
|
+
* timeout is not an error: the instance is force-released either way, it only
|
|
73
|
+
* means the log write may have lost the race.
|
|
74
|
+
*/
|
|
75
|
+
async function awaitSettled(entry, timeoutMs) {
|
|
76
|
+
const deadline = Date.now() + timeoutMs;
|
|
77
|
+
while (entry.inFlight && Date.now() < deadline)
|
|
78
|
+
await new Promise((r) => setTimeout(r, SETTLE_POLL_MS));
|
|
79
|
+
return !entry.inFlight;
|
|
80
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for the REAL-engine tests.
|
|
3
|
+
*
|
|
4
|
+
* Every helper builds a throwaway host: a temp data root, one registered
|
|
5
|
+
* workspace, and the app wired to the real adapter through `engineFactory`, so
|
|
6
|
+
* `harness.runtime` IS the engine (`RealRuntimeAdapter`), never a proxy. The
|
|
7
|
+
* engine's tool roots are pinned to the temp workspace, so the production path
|
|
8
|
+
* guard stays mounted and read-only tool calls are allowed inside it.
|
|
9
|
+
*
|
|
10
|
+
* W743: the engine is built by `createStudioEngine` (app.ts) — the SAME factory
|
|
11
|
+
* production uses. This file injects only paths/env/profile/LLM and must never
|
|
12
|
+
* grow a second assembly of its own (see `composition-root.test.ts`).
|
|
13
|
+
*/
|
|
14
|
+
import type { SessionEvent } from "@celestea/core";
|
|
15
|
+
import type { DisclosureOptions } from "./engine-plugins.js";
|
|
16
|
+
import { type StudioHarness } from "../harness.test-util.js";
|
|
17
|
+
import type { BusSubscription } from "../sse.js";
|
|
18
|
+
import type { RealRuntimeAdapter } from "./real-runtime-adapter.js";
|
|
19
|
+
import { type OfflineLlmOptions } from "./offline-llm.js";
|
|
20
|
+
export interface EngineHarnessOptions {
|
|
21
|
+
/** Sessions to plant: `name` -> events (written as cli-main.jsonl). */
|
|
22
|
+
sessions?: Record<string, readonly SessionEvent[]>;
|
|
23
|
+
/** Offline LLM options (script / inter-frame delay) for every generation. */
|
|
24
|
+
llm?: OfflineLlmOptions;
|
|
25
|
+
/** `session.json` of the planted sessions (`name` -> its keys, W729). */
|
|
26
|
+
meta?: Record<string, Record<string, string>>;
|
|
27
|
+
/**
|
|
28
|
+
* Environment for the HOST and for the engine this harness builds: the app's
|
|
29
|
+
* config and the real adapter's own knobs (`CELESTEA_WATCHDOG*`, tool roots,
|
|
30
|
+
* resource caps) both read it (W740).
|
|
31
|
+
*/
|
|
32
|
+
env?: NodeJS.ProcessEnv;
|
|
33
|
+
/** Files planted before the app composes (JSON-encoded; see `makeHarness`). */
|
|
34
|
+
files?: Record<string, unknown>;
|
|
35
|
+
/** Files planted VERBATIM (the worker table, a planted sidecar). */
|
|
36
|
+
rawFiles?: Record<string, string>;
|
|
37
|
+
/**
|
|
38
|
+
* W806 (P0): turn dynamic tool disclosure ON for the harness engine (absent =
|
|
39
|
+
* the static mode baseline every existing test composes).
|
|
40
|
+
*/
|
|
41
|
+
disclosure?: DisclosureOptions;
|
|
42
|
+
}
|
|
43
|
+
/** One complete turn in the engine's native JSONL shape. */
|
|
44
|
+
export declare function turnEvents(n: number, text?: string): SessionEvent[];
|
|
45
|
+
/** `count` complete turns, one per number. */
|
|
46
|
+
export declare function turns(count: number): SessionEvent[];
|
|
47
|
+
/** Plant a session directory with `cli-main.jsonl` (returns its absolute dir). */
|
|
48
|
+
export declare function plantSession(workspace: string, name: string, events: readonly SessionEvent[], meta?: Record<string, string>): string;
|
|
49
|
+
/** A turn is settled once the adapter reports idle again (+ one macrotask). */
|
|
50
|
+
export declare function waitIdle(h: StudioHarness, timeoutMs?: number): Promise<void>;
|
|
51
|
+
/** The harness's engine adapter (the real one). */
|
|
52
|
+
export declare function engineOf(h: StudioHarness): RealRuntimeAdapter;
|
|
53
|
+
/** One observed SSE frame (event name + envelope). */
|
|
54
|
+
export interface FrameRecord {
|
|
55
|
+
event: string;
|
|
56
|
+
turn: number;
|
|
57
|
+
seq: number;
|
|
58
|
+
payload: Record<string, unknown>;
|
|
59
|
+
}
|
|
60
|
+
/** Terminal status phases of a turn (the closing frame the host publishes). */
|
|
61
|
+
export declare const TERMINAL_PHASES: readonly string[];
|
|
62
|
+
/** The SSE payload is `unknown` on the wire; the host always sends an object. */
|
|
63
|
+
export declare function asPayload(value: unknown): Record<string, unknown>;
|
|
64
|
+
/** Drain frames until the turn's closing status frame (or fail loudly). */
|
|
65
|
+
export declare function collectUntilTerminal(sub: BusSubscription, frames: FrameRecord[], timeoutMs?: number): Promise<void>;
|
|
66
|
+
export interface TurnResult {
|
|
67
|
+
status: number;
|
|
68
|
+
body: Record<string, unknown>;
|
|
69
|
+
turn: number;
|
|
70
|
+
frames: FrameRecord[];
|
|
71
|
+
}
|
|
72
|
+
/** POST /api/turn while observing every SSE frame it produces. */
|
|
73
|
+
export declare function runTurnWithFrames(h: StudioHarness, input: string, timeoutMs?: number): Promise<TurnResult>;
|
|
74
|
+
/** Activate a session over the HTTP contract (the engine binds it on the turn). */
|
|
75
|
+
export declare function activate(h: StudioHarness, id: string): Promise<void>;
|
|
76
|
+
/** The session log file of a workspace session. */
|
|
77
|
+
export declare function readSessionLog(h: StudioHarness, name: string): string;
|
|
78
|
+
export declare function makeEngineHarness(opts?: EngineHarnessOptions): StudioHarness;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for the REAL-engine tests.
|
|
3
|
+
*
|
|
4
|
+
* Every helper builds a throwaway host: a temp data root, one registered
|
|
5
|
+
* workspace, and the app wired to the real adapter through `engineFactory`, so
|
|
6
|
+
* `harness.runtime` IS the engine (`RealRuntimeAdapter`), never a proxy. The
|
|
7
|
+
* engine's tool roots are pinned to the temp workspace, so the production path
|
|
8
|
+
* guard stays mounted and read-only tool calls are allowed inside it.
|
|
9
|
+
*
|
|
10
|
+
* W743: the engine is built by `createStudioEngine` (app.ts) — the SAME factory
|
|
11
|
+
* production uses. This file injects only paths/env/profile/LLM and must never
|
|
12
|
+
* grow a second assembly of its own (see `composition-root.test.ts`).
|
|
13
|
+
*/
|
|
14
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
16
|
+
import { serializeEventLog } from "@celestea/runtime";
|
|
17
|
+
import { createStudioEngine } from "../app.js";
|
|
18
|
+
import { jsonRequest, makeHarness } from "../harness.test-util.js";
|
|
19
|
+
import { createOfflineLlm } from "./offline-llm.js";
|
|
20
|
+
/** One complete turn in the engine's native JSONL shape. */
|
|
21
|
+
export function turnEvents(n, text = `答 ${n}`) {
|
|
22
|
+
const id = `turn-${n}`;
|
|
23
|
+
return [
|
|
24
|
+
{ type: "turn_start", id },
|
|
25
|
+
{ type: "user_message", text: `问 ${n}` },
|
|
26
|
+
{ type: "assistant_message", text },
|
|
27
|
+
{ type: "turn_end", id, outcome: "completed" },
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
/** `count` complete turns, one per number. */
|
|
31
|
+
export function turns(count) {
|
|
32
|
+
const out = [];
|
|
33
|
+
for (let i = 0; i < count; i++)
|
|
34
|
+
out.push(...turnEvents(i));
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
/** Plant a session directory with `cli-main.jsonl` (returns its absolute dir). */
|
|
38
|
+
export function plantSession(workspace, name, events, meta) {
|
|
39
|
+
const dir = join(workspace, name);
|
|
40
|
+
mkdirSync(dir, { recursive: true });
|
|
41
|
+
writeFileSync(join(dir, "cli-main.jsonl"), serializeEventLog(events));
|
|
42
|
+
if (meta !== undefined)
|
|
43
|
+
writeFileSync(join(dir, "session.json"), JSON.stringify(meta));
|
|
44
|
+
return dir;
|
|
45
|
+
}
|
|
46
|
+
/** A turn is settled once the adapter reports idle again (+ one macrotask). */
|
|
47
|
+
export async function waitIdle(h, timeoutMs = 5_000) {
|
|
48
|
+
const deadline = Date.now() + timeoutMs;
|
|
49
|
+
while (h.runtime.isBusy()) {
|
|
50
|
+
if (Date.now() > deadline)
|
|
51
|
+
throw new Error("turn did not settle in time");
|
|
52
|
+
await new Promise((r) => setTimeout(r, 2));
|
|
53
|
+
}
|
|
54
|
+
await new Promise((r) => setTimeout(r, 2));
|
|
55
|
+
}
|
|
56
|
+
/** The harness's engine adapter (the real one). */
|
|
57
|
+
export function engineOf(h) {
|
|
58
|
+
return h.runtime;
|
|
59
|
+
}
|
|
60
|
+
/** Terminal status phases of a turn (the closing frame the host publishes). */
|
|
61
|
+
export const TERMINAL_PHASES = ["completed", "cancelled", "error", "step_limit", "interrupted"];
|
|
62
|
+
function record(frame) {
|
|
63
|
+
return { event: frame.event, turn: frame.envelope.turn, seq: frame.envelope.seq, payload: asPayload(frame.envelope.payload) };
|
|
64
|
+
}
|
|
65
|
+
/** The SSE payload is `unknown` on the wire; the host always sends an object. */
|
|
66
|
+
export function asPayload(value) {
|
|
67
|
+
return (value ?? {});
|
|
68
|
+
}
|
|
69
|
+
/** Drain frames until the turn's closing status frame (or fail loudly). */
|
|
70
|
+
export async function collectUntilTerminal(sub, frames, timeoutMs = 5_000) {
|
|
71
|
+
const deadline = Date.now() + timeoutMs;
|
|
72
|
+
for (;;) {
|
|
73
|
+
const left = deadline - Date.now();
|
|
74
|
+
if (left <= 0)
|
|
75
|
+
throw new Error(`turn frames did not terminate (saw ${frames.map((f) => f.event).join(",")})`);
|
|
76
|
+
const frame = await Promise.race([sub.next(), new Promise((r) => setTimeout(() => r(null), left))]);
|
|
77
|
+
if (frame === null)
|
|
78
|
+
throw new Error("no frame before the deadline");
|
|
79
|
+
frames.push(record(frame));
|
|
80
|
+
if (frame.event === "status" && TERMINAL_PHASES.includes(String(asPayload(frame.envelope.payload)["phase"])))
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** POST /api/turn while observing every SSE frame it produces. */
|
|
85
|
+
export async function runTurnWithFrames(h, input, timeoutMs = 5_000) {
|
|
86
|
+
const sub = h.studio.services.bus.subscribe();
|
|
87
|
+
const frames = [];
|
|
88
|
+
const res = await h.app.request("/api/turn", jsonRequest("POST", { input }));
|
|
89
|
+
const body = (await res.json());
|
|
90
|
+
const turn = Number(body["turn"] ?? 0);
|
|
91
|
+
if (res.status === 202)
|
|
92
|
+
await collectUntilTerminal(sub, frames, timeoutMs);
|
|
93
|
+
sub.close();
|
|
94
|
+
await waitIdle(h);
|
|
95
|
+
return { status: res.status, body, turn, frames };
|
|
96
|
+
}
|
|
97
|
+
/** Activate a session over the HTTP contract (the engine binds it on the turn). */
|
|
98
|
+
export async function activate(h, id) {
|
|
99
|
+
const res = await h.app.request(`/api/sessions/${encodeURIComponent(id)}/activate`, jsonRequest("POST"));
|
|
100
|
+
if (res.status !== 200)
|
|
101
|
+
throw new Error(`activate ${id} failed: ${res.status} ${await res.text()}`);
|
|
102
|
+
}
|
|
103
|
+
/** The session log file of a workspace session. */
|
|
104
|
+
export function readSessionLog(h, name) {
|
|
105
|
+
return readFileSync(join(h.workspace, name, "cli-main.jsonl"), "utf8");
|
|
106
|
+
}
|
|
107
|
+
/** The startup profile of the OFFLINE engine (no provider registry involved). */
|
|
108
|
+
const OFFLINE_PROFILE = {
|
|
109
|
+
model: "offline-model",
|
|
110
|
+
base_url: "http://127.0.0.1:9/v1",
|
|
111
|
+
api_key_env: "CELESTEA_API_KEY",
|
|
112
|
+
reasoning_effort: null,
|
|
113
|
+
max_steps: 4096,
|
|
114
|
+
max_parallel_tool_calls: 4,
|
|
115
|
+
max_output_tokens: null,
|
|
116
|
+
context_window: 1_000_000,
|
|
117
|
+
system_prompt: "engine identity prompt",
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* The injected values of the harness engine (W743).
|
|
121
|
+
*
|
|
122
|
+
* W732 A1: this harness used to carry its OWN hand-copied copy of the engine
|
|
123
|
+
* assembly, and that copy had drifted — it never passed `grants` (so the W516
|
|
124
|
+
* boundary was composed as "no grants at all") and never passed `ledgerFile`
|
|
125
|
+
* (so no HTTP-layer test ever saw a usage row). It now injects PATHS/ENV/PROFILE
|
|
126
|
+
* only and reuses `createStudioEngine` (app.ts), the very function production
|
|
127
|
+
* runs: one assembly, two call sites.
|
|
128
|
+
*/
|
|
129
|
+
function offlineEngineDeps(opts, host) {
|
|
130
|
+
return (stores) => {
|
|
131
|
+
const wsPath = stores.workspaces.workspacePath("sample-ws");
|
|
132
|
+
const dataRoot = wsPath === undefined ? process.cwd() : dirname(wsPath);
|
|
133
|
+
return {
|
|
134
|
+
// The harness data root IS the directory of the workspace just mounted
|
|
135
|
+
// (`makeHarness` composes cwd = <root>), so the grants audit channel, the
|
|
136
|
+
// usage ledger and the fail-closed root rules agree with the HTTP layer.
|
|
137
|
+
workspacesFile: join(dataRoot, "workspaces.json"),
|
|
138
|
+
// W740: the real adapter reads the watchdog cadence from ITS env, so the
|
|
139
|
+
// harness env has to travel here as well (not only to the app).
|
|
140
|
+
env: { ...process.env, ...(wsPath === undefined ? {} : { CELESTEA_TOOL_ROOTS: wsPath }), ...(opts.env ?? {}) },
|
|
141
|
+
profile: OFFLINE_PROFILE,
|
|
142
|
+
providerLabel: null,
|
|
143
|
+
host,
|
|
144
|
+
llm: () => createOfflineLlm(opts.llm ?? {}),
|
|
145
|
+
...(opts.disclosure === undefined ? {} : { disclosure: opts.disclosure }),
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
export function makeEngineHarness(opts = {}) {
|
|
150
|
+
// W729: the per-session prompt hook needs the host services, which exist only
|
|
151
|
+
// AFTER composition — the same late-bound ref `app.ts` uses (see HostRef).
|
|
152
|
+
const host = { services: null };
|
|
153
|
+
const h = makeHarness({
|
|
154
|
+
engineFactory: createStudioEngine(offlineEngineDeps(opts, host)),
|
|
155
|
+
...(opts.files === undefined ? {} : { files: opts.files }),
|
|
156
|
+
...(opts.rawFiles === undefined ? {} : { rawFiles: opts.rawFiles }),
|
|
157
|
+
});
|
|
158
|
+
host.services = h.studio.services;
|
|
159
|
+
for (const [name, events] of Object.entries(opts.sessions ?? {}))
|
|
160
|
+
plantSession(h.workspace, name, events, opts.meta?.[name]);
|
|
161
|
+
return h;
|
|
162
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W740 — the watchdog's read-only face on the host.
|
|
3
|
+
*
|
|
4
|
+
* The watchdog itself lives in `packages/workers` (adjudication) and is mounted
|
|
5
|
+
* by `packages/runtime`'s composition root (cadence + teardown). What is left for
|
|
6
|
+
* the host is READING it, and this module is that reading, so the adapter stays
|
|
7
|
+
* about the HTTP contract:
|
|
8
|
+
*
|
|
9
|
+
* - `watchdogOf` / `watchdogRunningOf` — one session's sweep handle;
|
|
10
|
+
* - `watchdogCount` — how many live instances sweep (the `worker_status`
|
|
11
|
+
* projection's `watchdogs` field);
|
|
12
|
+
* - `workerStatusOf` — the `by_status` fold over the merged rows, tagged with
|
|
13
|
+
* that count.
|
|
14
|
+
*
|
|
15
|
+
* Everything here PEEKS. A session that was never composed is not composed just
|
|
16
|
+
* to answer a diagnostic: liveness must never be the thing that creates an engine.
|
|
17
|
+
*/
|
|
18
|
+
import type { SessionRuntime, SessionRuntimeRegistry } from "@celestea/runtime";
|
|
19
|
+
import type { Watchdog, WorkerRecoveryReport } from "@celestea/workers";
|
|
20
|
+
import type { WorkerSessionRow, WorkerStatusReport } from "../runtime-adapter.js";
|
|
21
|
+
/** The session's watchdog, or null when it has no instance / the watchdog is off. */
|
|
22
|
+
export declare function watchdogOf(registry: SessionRuntimeRegistry, session?: string | null): Watchdog | null;
|
|
23
|
+
/** Is this session's sweep timer running? */
|
|
24
|
+
export declare function watchdogRunningOf(registry: SessionRuntimeRegistry, session?: string | null): boolean;
|
|
25
|
+
/** Live instances currently sweeping their worker rows. */
|
|
26
|
+
export declare function watchdogCount(entries: readonly SessionRuntime[]): number;
|
|
27
|
+
/**
|
|
28
|
+
* The process-wide worker status: `by_status` counted from the merged rows (so a
|
|
29
|
+
* watchdog verdict moves it), the number of live sweepers, and — E §2.3 P0 ③ —
|
|
30
|
+
* the boot observer's judgement of the PERSISTED table (`stale[]` / `orphans[]`).
|
|
31
|
+
* The last two are PURE ADDITIONS to the frozen response shape.
|
|
32
|
+
*/
|
|
33
|
+
export declare function workerStatusOf(rows: readonly WorkerSessionRow[], watchdogs: number, wid?: string, recovery?: WorkerRecoveryReport | null): WorkerStatusReport;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W740 — the watchdog's read-only face on the host.
|
|
3
|
+
*
|
|
4
|
+
* The watchdog itself lives in `packages/workers` (adjudication) and is mounted
|
|
5
|
+
* by `packages/runtime`'s composition root (cadence + teardown). What is left for
|
|
6
|
+
* the host is READING it, and this module is that reading, so the adapter stays
|
|
7
|
+
* about the HTTP contract:
|
|
8
|
+
*
|
|
9
|
+
* - `watchdogOf` / `watchdogRunningOf` — one session's sweep handle;
|
|
10
|
+
* - `watchdogCount` — how many live instances sweep (the `worker_status`
|
|
11
|
+
* projection's `watchdogs` field);
|
|
12
|
+
* - `workerStatusOf` — the `by_status` fold over the merged rows, tagged with
|
|
13
|
+
* that count.
|
|
14
|
+
*
|
|
15
|
+
* Everything here PEEKS. A session that was never composed is not composed just
|
|
16
|
+
* to answer a diagnostic: liveness must never be the thing that creates an engine.
|
|
17
|
+
*/
|
|
18
|
+
import { aggregateWorkerStatus } from "./worker-bridge.js";
|
|
19
|
+
/** The session's watchdog, or null when it has no instance / the watchdog is off. */
|
|
20
|
+
export function watchdogOf(registry, session) {
|
|
21
|
+
return registry.peek(session ?? null)?.runtime.watchdog ?? null;
|
|
22
|
+
}
|
|
23
|
+
/** Is this session's sweep timer running? */
|
|
24
|
+
export function watchdogRunningOf(registry, session) {
|
|
25
|
+
return watchdogOf(registry, session)?.running ?? false;
|
|
26
|
+
}
|
|
27
|
+
/** Live instances currently sweeping their worker rows. */
|
|
28
|
+
export function watchdogCount(entries) {
|
|
29
|
+
let running = 0;
|
|
30
|
+
for (const entry of entries)
|
|
31
|
+
if (entry.runtime.watchdog?.running === true)
|
|
32
|
+
running += 1;
|
|
33
|
+
return running;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The process-wide worker status: `by_status` counted from the merged rows (so a
|
|
37
|
+
* watchdog verdict moves it), the number of live sweepers, and — E §2.3 P0 ③ —
|
|
38
|
+
* the boot observer's judgement of the PERSISTED table (`stale[]` / `orphans[]`).
|
|
39
|
+
* The last two are PURE ADDITIONS to the frozen response shape.
|
|
40
|
+
*/
|
|
41
|
+
export function workerStatusOf(rows, watchdogs, wid, recovery) {
|
|
42
|
+
const report = aggregateWorkerStatus(rows, wid);
|
|
43
|
+
const sweepers = watchdogs === 0 ? report : { ...report, watchdogs };
|
|
44
|
+
return recovery == null ? sweepers : { ...sweepers, stale: recovery.stale, orphans: recovery.orphans };
|
|
45
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker orchestration bridge: the `POST /api/worker/*` surface is a thin proxy
|
|
3
|
+
* over the engine's own worker tools (`src/api.rs:437-503`), and the two
|
|
4
|
+
* `GET` surfaces read the in-process registries directly.
|
|
5
|
+
*
|
|
6
|
+
* Keeping the mapping here means the adapter never re-implements orchestration:
|
|
7
|
+
* `spawn_worker` / `session_send_message` are DISPATCHED through the composed
|
|
8
|
+
* `ToolRegistry` (so the HTTP surface and the model's tool surface cannot
|
|
9
|
+
* drift), while `worker_status` reads the same registry summary the tool reads.
|
|
10
|
+
*
|
|
11
|
+
* W513: each session runtime owns its own registry, so the rows carry the
|
|
12
|
+
* owning host session (`host_session`) and the worker id / registry status /
|
|
13
|
+
* driver state — enough for `GET /api/sessions` to list workers per session and
|
|
14
|
+
* for the host to aggregate a process-wide `worker_status`.
|
|
15
|
+
*/
|
|
16
|
+
import { type ToolRegistry } from "@celestea/core";
|
|
17
|
+
import { type WorkerRegistry } from "@celestea/workers";
|
|
18
|
+
import type { WorkerSessionRow, WorkerSpawnOutcome, WorkerStatusReport } from "../runtime-adapter.js";
|
|
19
|
+
/**
|
|
20
|
+
* Engine-memory worker sessions (`worker:<sid>`, pseudo-workspace "engine").
|
|
21
|
+
*
|
|
22
|
+
* W740: the row is ENTRY-driven, not conversation-driven. A settled worker gives
|
|
23
|
+
* up its conversation (W736's receipt path; the watchdog's F2 release), so a
|
|
24
|
+
* projection that only walked `registry.sessions.metas()` dropped every
|
|
25
|
+
* DONE/FAILED worker out of the panel the moment it finished — exactly the
|
|
26
|
+
* opposite of what a status view is for. Each registry row emits a panel row,
|
|
27
|
+
* enriched with the live conversation while one still exists.
|
|
28
|
+
*/
|
|
29
|
+
export declare function workerSessionsOf(registry: WorkerRegistry | null, hostSessionId: string | null): WorkerSessionRow[];
|
|
30
|
+
/** Process-wide `worker_status` fold over the merged rows (W513). */
|
|
31
|
+
export declare function aggregateWorkerStatus(rows: readonly WorkerSessionRow[], wid?: string): WorkerStatusReport;
|
|
32
|
+
/** Studio projection of a worker session transcript (null = unknown session). */
|
|
33
|
+
/**
|
|
34
|
+
* W769: the runtime facts these aggregations need about ONE session instance —
|
|
35
|
+
* structural, so the adapter passes its own `SessionRuntime` rows straight in and
|
|
36
|
+
* this module never imports the runtime package (it stays a pure view layer).
|
|
37
|
+
*/
|
|
38
|
+
export interface WorkerHostEntry {
|
|
39
|
+
sessionId: string | null;
|
|
40
|
+
dir: string | null;
|
|
41
|
+
inFlight: boolean;
|
|
42
|
+
runtime: {
|
|
43
|
+
workers: WorkerRegistry | null;
|
|
44
|
+
hostSessionId: string | null;
|
|
45
|
+
tools: ToolRegistry | null;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** Merged worker rows over every live instance (W513 aggregate view). */
|
|
49
|
+
export declare function mergedWorkerRows(entries: readonly WorkerHostEntry[]): WorkerSessionRow[];
|
|
50
|
+
/** The messages of one worker session, from whichever live instance owns it. */
|
|
51
|
+
export declare function workerMessagesAcross(entries: readonly WorkerHostEntry[], sessionId: string): unknown[] | null;
|
|
52
|
+
/** `POST /api/worker/spawn`: the spawn tool, dispatched through the session's own registry. */
|
|
53
|
+
export declare function spawnWorkerThrough(entry: WorkerHostEntry, req: {
|
|
54
|
+
wid: string;
|
|
55
|
+
brief: string;
|
|
56
|
+
title?: string;
|
|
57
|
+
model?: string;
|
|
58
|
+
report_to?: string;
|
|
59
|
+
}, callId: string): Promise<WorkerSpawnOutcome>;
|
|
60
|
+
/**
|
|
61
|
+
* `POST /api/worker/send`: the worker's registry is per session, so the message is
|
|
62
|
+
* routed to the instance that owns the target — the first one that accepts it wins.
|
|
63
|
+
*/
|
|
64
|
+
export declare function sendWorkerThrough(entries: readonly WorkerHostEntry[], req: {
|
|
65
|
+
target: string;
|
|
66
|
+
content: string;
|
|
67
|
+
}, callId: () => string): Promise<Record<string, unknown>>;
|
|
68
|
+
export declare function workerMessagesOf(registry: WorkerRegistry | null, sessionId: string): unknown[] | null;
|
|
69
|
+
/** Dispatch one worker tool through the composed registry. */
|
|
70
|
+
export declare function dispatchWorkerTool(registry: ToolRegistry | null, name: string, args: Record<string, unknown>, callId: string): Promise<Record<string, unknown> | null>;
|
|
71
|
+
/** `spawn_worker` value envelope -> the HTTP outcome (`{ok, sessionId, title, wid}`). */
|
|
72
|
+
export declare function spawnOutcomeOf(value: Record<string, unknown> | null): WorkerSpawnOutcome;
|
|
73
|
+
/** `send_message` value envelope -> the response body (verbatim). */
|
|
74
|
+
export declare function sendBodyOf(value: Record<string, unknown> | null): Record<string, unknown>;
|
|
75
|
+
/** `worker_status` tool/registry payload -> the frozen HTTP shape. */
|
|
76
|
+
export declare function toStatusReport(raw: Record<string, unknown> | null, wid: string | undefined): WorkerStatusReport;
|