wicked-crew 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/audit.d.ts +13 -0
- package/dist/api/audit.d.ts.map +1 -1
- package/dist/api/audit.js +18 -2
- package/dist/api/audit.js.map +1 -1
- package/dist/api/guidance-index.d.ts +39 -0
- package/dist/api/guidance-index.d.ts.map +1 -0
- package/dist/api/guidance-index.js +67 -0
- package/dist/api/guidance-index.js.map +1 -0
- package/dist/api/open-path.d.ts +16 -0
- package/dist/api/open-path.d.ts.map +1 -1
- package/dist/api/open-path.js +22 -0
- package/dist/api/open-path.js.map +1 -1
- package/dist/api/retry-index.d.ts +30 -0
- package/dist/api/retry-index.d.ts.map +1 -0
- package/dist/api/retry-index.js +45 -0
- package/dist/api/retry-index.js.map +1 -0
- package/dist/api/routes.d.ts +53 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +352 -23
- package/dist/api/routes.js.map +1 -1
- package/dist/api/run-files.d.ts +63 -0
- package/dist/api/run-files.d.ts.map +1 -0
- package/dist/api/run-files.js +271 -0
- package/dist/api/run-files.js.map +1 -0
- package/dist/api/server.d.ts +79 -0
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +135 -6
- package/dist/api/server.js.map +1 -1
- package/dist/api/stall-watchdog.d.ts +62 -0
- package/dist/api/stall-watchdog.d.ts.map +1 -0
- package/dist/api/stall-watchdog.js +138 -0
- package/dist/api/stall-watchdog.js.map +1 -0
- package/dist/cli/index.js +78 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/core/adapter.d.ts +24 -10
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +191 -30
- package/dist/core/adapter.js.map +1 -1
- package/dist/core/bridge-reaper.d.ts +134 -0
- package/dist/core/bridge-reaper.d.ts.map +1 -0
- package/dist/core/bridge-reaper.js +286 -0
- package/dist/core/bridge-reaper.js.map +1 -0
- package/dist/core/deliver.d.ts +118 -0
- package/dist/core/deliver.d.ts.map +1 -0
- package/dist/core/deliver.js +241 -0
- package/dist/core/deliver.js.map +1 -0
- package/dist/core/deliverable-floor.d.ts +103 -0
- package/dist/core/deliverable-floor.d.ts.map +1 -0
- package/dist/core/deliverable-floor.js +173 -0
- package/dist/core/deliverable-floor.js.map +1 -0
- package/dist/core/exec.d.ts +2 -0
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js.map +1 -1
- package/dist/core/types.d.ts +79 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -1
- package/dist/interactive/bridge-pool.d.ts +28 -0
- package/dist/interactive/bridge-pool.d.ts.map +1 -1
- package/dist/interactive/bridge-pool.js +67 -10
- package/dist/interactive/bridge-pool.js.map +1 -1
- package/dist/interactive/chat-events.d.ts +207 -0
- package/dist/interactive/chat-events.d.ts.map +1 -0
- package/dist/interactive/chat-events.js +769 -0
- package/dist/interactive/chat-events.js.map +1 -0
- package/dist/interactive/demo-events.d.ts +283 -0
- package/dist/interactive/demo-events.d.ts.map +1 -0
- package/dist/interactive/demo-events.js +889 -0
- package/dist/interactive/demo-events.js.map +1 -0
- package/dist/interactive/draft-events.d.ts +87 -7
- package/dist/interactive/draft-events.d.ts.map +1 -1
- package/dist/interactive/draft-events.js +352 -49
- package/dist/interactive/draft-events.js.map +1 -1
- package/dist/interactive/edit-events.d.ts +22 -0
- package/dist/interactive/edit-events.d.ts.map +1 -1
- package/dist/interactive/edit-events.js +73 -2
- package/dist/interactive/edit-events.js.map +1 -1
- package/dist/interactive/repo-snapshot.d.ts +100 -0
- package/dist/interactive/repo-snapshot.d.ts.map +1 -0
- package/dist/interactive/repo-snapshot.js +289 -0
- package/dist/interactive/repo-snapshot.js.map +1 -0
- package/dist/projects/graph-paths.d.ts +92 -0
- package/dist/projects/graph-paths.d.ts.map +1 -0
- package/dist/projects/graph-paths.js +130 -0
- package/dist/projects/graph-paths.js.map +1 -0
- package/dist/projects/graph.d.ts +179 -0
- package/dist/projects/graph.d.ts.map +1 -0
- package/dist/projects/graph.js +775 -0
- package/dist/projects/graph.js.map +1 -0
- package/dist/projects/routes.d.ts +7 -0
- package/dist/projects/routes.d.ts.map +1 -1
- package/dist/projects/routes.js +122 -0
- package/dist/projects/routes.js.map +1 -1
- package/dist/studio/assets/index-8p8uwCxG.js +530 -0
- package/dist/studio/assets/index-D6S9zUtO.css +32 -0
- package/dist/studio/index.html +5 -3
- package/package.json +3 -3
- package/dist/studio/assets/index-CCwXa1cn.js +0 -428
- package/dist/studio/assets/index-HWxo0h41.css +0 -32
package/dist/core/types.d.ts
CHANGED
|
@@ -55,6 +55,84 @@ export interface LaunchRunInput {
|
|
|
55
55
|
* fails the launch with no session persisted. Omit for runs that deliver inside their workdir.
|
|
56
56
|
*/
|
|
57
57
|
extraWriteRoots?: string[];
|
|
58
|
+
/**
|
|
59
|
+
* The PROJECT's co-located code graph this run's governed workers should query instead of the
|
|
60
|
+
* run repo's own — resolved by `projects/graph.ts::resolveProjectGraphBinding`, which is the ONE
|
|
61
|
+
* thing that knows where a project graph lives and what each repo is labelled inside it.
|
|
62
|
+
*
|
|
63
|
+
* `repoLabel` is optional in the TYPE because a repo-LESS run legitimately has none — there is
|
|
64
|
+
* no own repo to name. It is not optional in MEANING for a repo-bound run: the engine uses it to
|
|
65
|
+
* confirm the co-located graph actually holds this run's repo.
|
|
66
|
+
*
|
|
67
|
+
* TypeScript cannot catch a missing label here — the invariant ties this field to `repoRef`, a
|
|
68
|
+
* sibling field — so `CoreAdapter.launchRun` enforces it at RUNTIME. Two failure modes, and they
|
|
69
|
+
* are NOT the same thing:
|
|
70
|
+
*
|
|
71
|
+
* - **crew refuses the launch outright.** Passing `projectGraph` without `projectId`, or with
|
|
72
|
+
* `repoRef` set and no `repoLabel`, THROWS: no session is created and nothing runs. These are
|
|
73
|
+
* caller bugs with no sensible degraded reading, so they fail before anything is persisted.
|
|
74
|
+
* - **the ENGINE declines the binding.** A well-formed binding whose graph turns out not to hold
|
|
75
|
+
* the named label is refused engine-side and the run proceeds against its own repo graph — a
|
|
76
|
+
* real launch, quietly narrower. That is the fallback; it is not what a missing `repoLabel`
|
|
77
|
+
* gets you.
|
|
78
|
+
*
|
|
79
|
+
* Omit the whole field for the per-repo behaviour.
|
|
80
|
+
*/
|
|
81
|
+
projectGraph?: {
|
|
82
|
+
dbPath: string;
|
|
83
|
+
repoLabel?: string;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Delivery mode (crew#293). `"pr"` ⇒ the adapter appends the hardened deliver Tool phase
|
|
87
|
+
* (push the run branch + `gh pr create`, see `core/deliver.ts`) to a PER-RUN copy of the
|
|
88
|
+
* selected workflow def and launches that copy — the shared def is never mutated. Requires
|
|
89
|
+
* `workflow`; a free-text run has no def to append to. Omit ⇒ no delivery phase (default).
|
|
90
|
+
*/
|
|
91
|
+
deliver?: 'pr';
|
|
92
|
+
/**
|
|
93
|
+
* THE DELIVERABLE FLOOR (crew#311) — absolute paths this run MUST have produced by the time
|
|
94
|
+
* it finishes. When non-empty (and `workflow` is set), the adapter appends a deterministic
|
|
95
|
+
* verification Tool phase to a PER-RUN copy of the def (see `core/deliverable-floor.ts`)
|
|
96
|
+
* which re-derives "done" from the artifacts: every declared path must exist and carry bytes,
|
|
97
|
+
* or the phase exits non-zero and the RUN FAILS, naming what was expected and what was found.
|
|
98
|
+
*
|
|
99
|
+
* This is the instrument for runs whose deliverable is a FILE in a declared write root rather
|
|
100
|
+
* than a worktree diff — every crew interactive seam. The engine's own two floors do not
|
|
101
|
+
* reach them: the built-in evidence floor re-verifies a git diff and is fail-closed on a
|
|
102
|
+
* repo-less run, and `required_deliverables` is checked only by the wrapped-CLI runner and
|
|
103
|
+
* only against the unit's cwd (an absolute path counts as missing there by construction).
|
|
104
|
+
*
|
|
105
|
+
* Omit for runs whose evidence is the worktree diff — those are the engine's to floor.
|
|
106
|
+
*/
|
|
107
|
+
requireDeliverables?: string[];
|
|
108
|
+
}
|
|
109
|
+
/** Default `workerStallMinutes` (crew#287): silent minutes before the stall watchdog fires. */
|
|
110
|
+
export declare const DEFAULT_WORKER_STALL_MINUTES = 15;
|
|
111
|
+
/**
|
|
112
|
+
* LOCAL extension of the published `SystemSettings` (wicked-crew-api-types 0.6.0): the crew#287
|
|
113
|
+
* stall-watchdog knob is daemon-owned until the contract picks it up. NOTE for the next
|
|
114
|
+
* api-types release: fold `workerStallMinutes` into `SystemSettings` (and the synthetic
|
|
115
|
+
* `workerStalled` /ws frame — see `api/stall-watchdog.ts` `WorkerStalledFrame` — into the
|
|
116
|
+
* event documentation). Extra fields are forward-additive on the wire (DES-STUDIO-001 §5.1),
|
|
117
|
+
* so shipping it daemon-side first breaks no consumer.
|
|
118
|
+
*/
|
|
119
|
+
export interface CrewSystemSettings extends SystemSettings {
|
|
120
|
+
/**
|
|
121
|
+
* Minutes a run in `executing` may go without ANY engine event on the daemon's relay before
|
|
122
|
+
* a synthetic `workerStalled` frame is broadcast on /ws (detection only; default 15).
|
|
123
|
+
*/
|
|
124
|
+
workerStallMinutes?: number;
|
|
125
|
+
/**
|
|
126
|
+
* Skin-owned preference blobs, round-tripped verbatim (crew#323). RESTATED here rather than
|
|
127
|
+
* merely inherited from `SystemSettings` so the daemon's own type SAYS the settings store is
|
|
128
|
+
* shared with the experience plane — the fact that used to live only in a comment in the
|
|
129
|
+
* studio's client, which is how `studio.appearance` / `studio.notifications` could be
|
|
130
|
+
* silently dropped by `PUT /settings` without either side's types objecting.
|
|
131
|
+
*
|
|
132
|
+
* The daemon never interprets a value here; it enforces only "JSON-serializable, under the
|
|
133
|
+
* per-key byte cap" at the PUT boundary (`api/routes.ts` `STUDIO_SETTINGS_MAX_BYTES`).
|
|
134
|
+
*/
|
|
135
|
+
[key: `studio.${string}`]: unknown;
|
|
58
136
|
}
|
|
59
|
-
export declare const DEFAULT_SETTINGS:
|
|
137
|
+
export declare const DEFAULT_SETTINGS: CrewSystemSettings;
|
|
60
138
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/core/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,mBAAmB,uBAAuB,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,8FAA8F;AAC9F,MAAM,WAAW,cAAc;IAC7B,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,mBAAmB,uBAAuB,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,8FAA8F;AAC9F,MAAM,WAAW,cAAc;IAC7B,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,YAAY,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD;;;;;OAKG;IACH,OAAO,CAAC,EAAE,IAAI,CAAC;IACf;;;;;;;;;;;;;;OAcG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,+FAA+F;AAC/F,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;OASG;IACH,CAAC,GAAG,EAAE,UAAU,MAAM,EAAE,GAAG,OAAO,CAAC;CACpC;AAED,eAAO,MAAM,gBAAgB,EAAE,kBAG9B,CAAC"}
|
package/dist/core/types.js
CHANGED
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
* daemon speaks these daemon-owned shapes, which is what quarantines the
|
|
19
19
|
* FINALIZING core-ts `subscribe` seam to a single file (DES-STUDIO-001 §5.2).
|
|
20
20
|
*/
|
|
21
|
+
/** Default `workerStallMinutes` (crew#287): silent minutes before the stall watchdog fires. */
|
|
22
|
+
export const DEFAULT_WORKER_STALL_MINUTES = 15;
|
|
21
23
|
export const DEFAULT_SETTINGS = {
|
|
22
24
|
graphNodeLimit: 150,
|
|
25
|
+
workerStallMinutes: DEFAULT_WORKER_STALL_MINUTES,
|
|
23
26
|
};
|
|
24
27
|
//# sourceMappingURL=types.js.map
|
package/dist/core/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA2FH,+FAA+F;AAC/F,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,CAAC;AA6B/C,MAAM,CAAC,MAAM,gBAAgB,GAAuB;IAClD,cAAc,EAAE,GAAG;IACnB,kBAAkB,EAAE,4BAA4B;CACjD,CAAC"}
|
|
@@ -19,8 +19,14 @@
|
|
|
19
19
|
* live pid earns three 1.5 s attempts before the bridge is declared dead. Identity is checked
|
|
20
20
|
* too: `/api/health` must report THIS root, or a recycled port belonging to some other service
|
|
21
21
|
* would be proxied as if it were ours.
|
|
22
|
+
*
|
|
23
|
+
* On start/adopt the pool also records the daemon's own origin with the bridge (crew#298,
|
|
24
|
+
* `POST /api/studio-origin`, interactive ≥ 0.8.0) so the bridge's `GET /` redirects a direct
|
|
25
|
+
* visitor into studio instead of its API-only fallback page. Fire-and-forget, once per pooled
|
|
26
|
+
* bridge: recording can never fail — or slow down — a proxied request.
|
|
22
27
|
*/
|
|
23
28
|
import { type ChildProcess } from 'node:child_process';
|
|
29
|
+
import type { AddressInfo } from 'node:net';
|
|
24
30
|
export declare const LOCK_NAME = ".wi-serve.json";
|
|
25
31
|
/** ADR-0025: 1.5 s × 3 while the pid lives. */
|
|
26
32
|
export declare const HEALTH_TIMEOUT_MS = 1500;
|
|
@@ -45,7 +51,21 @@ export interface BridgePoolIo {
|
|
|
45
51
|
startTimeoutMs?: number;
|
|
46
52
|
healthTimeoutMs?: number;
|
|
47
53
|
log?: (msg: string) => void;
|
|
54
|
+
/** Lower-severity channel for EXPECTED skips (a pre-0.8.0 bridge without the endpoint, #298). */
|
|
55
|
+
debug?: (msg: string) => void;
|
|
56
|
+
/**
|
|
57
|
+
* The daemon's own origin (`http://<bound host>:<bound port>`), resolved LAZILY — the pool is
|
|
58
|
+
* built before `listen`, but only consulted while serving a request, i.e. after the address is
|
|
59
|
+
* bound. Null (or absent) means "nothing to record" and the pool never POSTs (#298).
|
|
60
|
+
*/
|
|
61
|
+
studioOrigin?: () => string | null;
|
|
48
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* The daemon's own http origin from its bound server address, or null before `listen`. Wildcard
|
|
65
|
+
* binds (`0.0.0.0` / `::`) are not dialable from a browser, so they normalize to loopback — the
|
|
66
|
+
* bridge is local-only anyway, so whoever hits its port can reach the daemon at 127.0.0.1 too.
|
|
67
|
+
*/
|
|
68
|
+
export declare function boundOrigin(addr: AddressInfo | string | null): string | null;
|
|
49
69
|
/** `<root>/.wi-serve.json`, or null when absent/unparseable/incomplete. */
|
|
50
70
|
export declare function readLock(root: string): LiveBridge | null;
|
|
51
71
|
/** Signal 0 probes existence without delivering: EPERM means alive-but-not-ours. */
|
|
@@ -64,6 +84,14 @@ export declare class InteractiveBridgePool {
|
|
|
64
84
|
/** Live bridges, for tests and future operator introspection. */
|
|
65
85
|
keys(): string[];
|
|
66
86
|
private resolveOrStart;
|
|
87
|
+
/**
|
|
88
|
+
* POST the daemon's own origin to the bridge's `/api/studio-origin` (#298), so the bridge's
|
|
89
|
+
* `GET /` redirects a direct visitor into studio instead of the API-only fallback page.
|
|
90
|
+
* At most one attempt per pooled bridge per process; 404/405 means the bridge predates the
|
|
91
|
+
* endpoint (interactive < 0.8.0) and is an expected skip, any other failure is a warn — never
|
|
92
|
+
* an error, because origin recording must never fail a proxy request.
|
|
93
|
+
*/
|
|
94
|
+
private recordStudioOrigin;
|
|
67
95
|
/** The lockfile points at a bridge that is alive, answering, and serving THIS root. */
|
|
68
96
|
private healthy;
|
|
69
97
|
private start;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-pool.d.ts","sourceRoot":"","sources":["../../src/interactive/bridge-pool.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"bridge-pool.d.ts","sourceRoot":"","sources":["../../src/interactive/bridge-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,eAAO,MAAM,SAAS,mBAAmB,CAAC;AAC1C,+CAA+C;AAC/C,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,6FAA6F;AAC7F,eAAO,MAAM,gBAAgB,QAAS,CAAC;AAEvC,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,sFAAsF;AACtF,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,qFAAqF;IACrF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAK1C;AAOD,gGAAgG;AAChG,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,CAAC;IACvC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,iGAAiG;IACjG,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAS5E;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAQxD;AAED,oFAAoF;AACpF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ7C;AAyBD,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAe;IAClC,oGAAoG;IACpG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmC;IACxD,6FAA6F;IAC7F,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0C;gBAEvD,EAAE,GAAE,YAAiB;IAIjC,qGAAqG;IAC/F,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAc/C,uFAAuF;IACvF,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI9B,iEAAiE;IACjE,IAAI,IAAI,MAAM,EAAE;YAIF,cAAc;IAW5B;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB;IA+B1B,uFAAuF;YACzE,OAAO;YAeP,KAAK;CAsCpB"}
|
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
* live pid earns three 1.5 s attempts before the bridge is declared dead. Identity is checked
|
|
20
20
|
* too: `/api/health` must report THIS root, or a recycled port belonging to some other service
|
|
21
21
|
* would be proxied as if it were ours.
|
|
22
|
+
*
|
|
23
|
+
* On start/adopt the pool also records the daemon's own origin with the bridge (crew#298,
|
|
24
|
+
* `POST /api/studio-origin`, interactive ≥ 0.8.0) so the bridge's `GET /` redirects a direct
|
|
25
|
+
* visitor into studio instead of its API-only fallback page. Fire-and-forget, once per pooled
|
|
26
|
+
* bridge: recording can never fail — or slow down — a proxied request.
|
|
22
27
|
*/
|
|
23
28
|
import { spawn as nodeSpawn } from 'node:child_process';
|
|
24
29
|
import { mkdirSync, readFileSync } from 'node:fs';
|
|
@@ -43,6 +48,21 @@ export class BridgeUnavailableError extends Error {
|
|
|
43
48
|
function serveCommand(root) {
|
|
44
49
|
return `npx wicked-interactive serve --root ${root}`;
|
|
45
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* The daemon's own http origin from its bound server address, or null before `listen`. Wildcard
|
|
53
|
+
* binds (`0.0.0.0` / `::`) are not dialable from a browser, so they normalize to loopback — the
|
|
54
|
+
* bridge is local-only anyway, so whoever hits its port can reach the daemon at 127.0.0.1 too.
|
|
55
|
+
*/
|
|
56
|
+
export function boundOrigin(addr) {
|
|
57
|
+
if (addr === null || typeof addr === 'string')
|
|
58
|
+
return null;
|
|
59
|
+
const host = addr.address === '' || addr.address === '0.0.0.0' || addr.address === '::'
|
|
60
|
+
? '127.0.0.1'
|
|
61
|
+
: addr.address.includes(':')
|
|
62
|
+
? `[${addr.address}]`
|
|
63
|
+
: addr.address;
|
|
64
|
+
return `http://${host}:${addr.port}`;
|
|
65
|
+
}
|
|
46
66
|
/** `<root>/.wi-serve.json`, or null when absent/unparseable/incomplete. */
|
|
47
67
|
export function readLock(root) {
|
|
48
68
|
try {
|
|
@@ -97,8 +117,8 @@ export class InteractiveBridgePool {
|
|
|
97
117
|
// Fast path: we started/adopted it and its pid is still alive. A full health round-trip on
|
|
98
118
|
// every proxied request would put a 1.5 s timeout budget in front of every asset fetch.
|
|
99
119
|
const cached = this.live.get(root);
|
|
100
|
-
if (cached && pidAlive(cached.pid))
|
|
101
|
-
return cached;
|
|
120
|
+
if (cached && pidAlive(cached.bridge.pid))
|
|
121
|
+
return cached.bridge;
|
|
102
122
|
this.live.delete(root);
|
|
103
123
|
const pending = this.inflight.get(root);
|
|
104
124
|
if (pending)
|
|
@@ -116,14 +136,51 @@ export class InteractiveBridgePool {
|
|
|
116
136
|
return [...this.live.keys()];
|
|
117
137
|
}
|
|
118
138
|
async resolveOrStart(root) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
this.
|
|
126
|
-
return
|
|
139
|
+
// Adopt-or-start; either way the bridge just answered `/api/health` for this root, which is
|
|
140
|
+
// exactly the moment #298 wants the studio origin recorded — fire-and-forget, so recording
|
|
141
|
+
// can never delay (let alone fail) the proxied request that triggered the resolution.
|
|
142
|
+
const bridge = (await this.healthy(root)) ?? (await this.start(root));
|
|
143
|
+
const entry = { bridge, originRecorded: false };
|
|
144
|
+
this.live.set(root, entry);
|
|
145
|
+
this.recordStudioOrigin(entry);
|
|
146
|
+
return bridge;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* POST the daemon's own origin to the bridge's `/api/studio-origin` (#298), so the bridge's
|
|
150
|
+
* `GET /` redirects a direct visitor into studio instead of the API-only fallback page.
|
|
151
|
+
* At most one attempt per pooled bridge per process; 404/405 means the bridge predates the
|
|
152
|
+
* endpoint (interactive < 0.8.0) and is an expected skip, any other failure is a warn — never
|
|
153
|
+
* an error, because origin recording must never fail a proxy request.
|
|
154
|
+
*/
|
|
155
|
+
recordStudioOrigin(entry) {
|
|
156
|
+
if (entry.originRecorded)
|
|
157
|
+
return;
|
|
158
|
+
entry.originRecorded = true;
|
|
159
|
+
const origin = this.io.studioOrigin?.() ?? null;
|
|
160
|
+
if (origin === null)
|
|
161
|
+
return; // no origin to record (pool not bootstrapped from a listening daemon)
|
|
162
|
+
const { host, port } = entry.bridge;
|
|
163
|
+
const timeout = this.io.healthTimeoutMs ?? HEALTH_TIMEOUT_MS;
|
|
164
|
+
void (async () => {
|
|
165
|
+
try {
|
|
166
|
+
const res = await fetch(`http://${host}:${port}/api/studio-origin`, {
|
|
167
|
+
method: 'POST',
|
|
168
|
+
headers: { 'content-type': 'application/json' },
|
|
169
|
+
body: JSON.stringify({ origin }),
|
|
170
|
+
signal: AbortSignal.timeout(timeout),
|
|
171
|
+
});
|
|
172
|
+
await res.arrayBuffer().catch(() => undefined); // drain, so the connection is released
|
|
173
|
+
if (res.status === 404 || res.status === 405) {
|
|
174
|
+
this.io.debug?.(`bridge at ${host}:${port} has no /api/studio-origin (interactive < 0.8.0) — skipping origin record`);
|
|
175
|
+
}
|
|
176
|
+
else if (!res.ok) {
|
|
177
|
+
this.io.log?.(`recording studio origin ${origin} with the bridge at ${host}:${port} failed: HTTP ${res.status}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
this.io.log?.(`recording studio origin ${origin} with the bridge at ${host}:${port} failed: ${err.message}`);
|
|
182
|
+
}
|
|
183
|
+
})();
|
|
127
184
|
}
|
|
128
185
|
/** The lockfile points at a bridge that is alive, answering, and serving THIS root. */
|
|
129
186
|
async healthy(root) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-pool.js","sourceRoot":"","sources":["../../src/interactive/bridge-pool.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"bridge-pool.js","sourceRoot":"","sources":["../../src/interactive/bridge-pool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in governed answering of wicked-interactive's conversational ITERATION asks (CREW-UX-5 —
|
|
3
|
+
* the third interactive leg, beside draft-events.ts and edit-events.ts).
|
|
4
|
+
*
|
|
5
|
+
* A finished doc's thread send in wicked-studio does `postFork` (mints a version) and injects
|
|
6
|
+
* `wicked.interactive.chat.posted` — and NOTHING consumed that topic since the ad-hoc assist
|
|
7
|
+
* agent retired: crew answers `doc.created` (the first draft) and `feedback.processed` (the
|
|
8
|
+
* structural handoff), but the plain conversational ask ("make the intro punchier") had no
|
|
9
|
+
* answerer, so BRIEF-UX-001 J3's "iterate twice" was impossible. This module makes a
|
|
10
|
+
* crew-governed run the answerer: understand-the-ask → revise, announced back on the SAME
|
|
11
|
+
* `draft.completed` wire the first draft rides — the service lands the revised full HTML as a
|
|
12
|
+
* generated version (`materializeDraft` → `applyGeneratedHtml`), which is exactly what an
|
|
13
|
+
* iteration is.
|
|
14
|
+
*
|
|
15
|
+
* Shape mirrors draft-events.ts (dynamic wicked-bus import, graceful degradation, durable
|
|
16
|
+
* cursor `cursor_init: 'latest'` under a dedicated plugin name, durable replay-dedup ledger,
|
|
17
|
+
* `wi-crew` narration via status.posted). The chat-specific deltas:
|
|
18
|
+
*
|
|
19
|
+
* - THE TRIGGER IS A CONVERSATION LINE, so the actionable filter is layered (the contract):
|
|
20
|
+
* (a) `role: "user"` only — agent narration echoes also ride chat.posted;
|
|
21
|
+
* (b) the doc must EXIST under the resolved docs root and not belong to a foreign loop —
|
|
22
|
+
* and the disk truth here is subtle: interactive's `initManifest` only ever RECORDS a
|
|
23
|
+
* `kind` for demo docs (a real `kind: "source"` doc's versions.json carries NO kind
|
|
24
|
+
* field at all — the "source" spelling rides the doc.created EVENT only; verified
|
|
25
|
+
* against interactive 0.8.0 and main, src/service/server.js `initWorkspace(dir, html)`).
|
|
26
|
+
* So the gate accepts `source` (future manifests may record it) and the absent-default
|
|
27
|
+
* `doc`, and rejects explicit foreign kinds (`demo`) — a doc we cannot see is not ours
|
|
28
|
+
* to answer either;
|
|
29
|
+
* (c) per-doc serialization — an ask on a doc whose draft/edit/chat run is still in flight
|
|
30
|
+
* is QUEUED (FIFO per doc), never raced: two concurrent revisions of one doc would
|
|
31
|
+
* land as two forks of the same parent and the second would silently drop the first;
|
|
32
|
+
* (d) the text must be an ASK, not an echo of a machine-composed message — the feedback
|
|
33
|
+
* overlay injects its batch as a chat.posted TOO (same `source_message_id` as its
|
|
34
|
+
* `feedback.submitted`), and that batch is already the edit seam's business.
|
|
35
|
+
* - VERSION SNAPSHOT AT LAUNCH: the seam copies the doc's CURRENT HEAD html into the chat
|
|
36
|
+
* inbox and names the COPY in the task, so (1) the worker never needs read access to the
|
|
37
|
+
* doc workspace (the one declared write root covers input + deliverable — crew#263 /
|
|
38
|
+
* wicked-core#259: write roots are readable) and (2) a queued second ask snapshots the
|
|
39
|
+
* head AFTER the first revision landed — which is what "iterate twice" means.
|
|
40
|
+
* - THE LANDING GATE: `draft.completed` is announced by path and the service lands the new
|
|
41
|
+
* version ASYNCHRONOUSLY. A queued ask drained the instant our run completes would snapshot
|
|
42
|
+
* the stale head and silently drop the revision the user just watched land. So a successful
|
|
43
|
+
* completion arms a per-doc gate — drain only once the manifest head ADVANCES past the head
|
|
44
|
+
* we launched from, or a timeout passes (the service may be down; waiting forever would
|
|
45
|
+
* strand the queue).
|
|
46
|
+
* - IDEMPOTENCY KEYING: one doc legitimately produces many asks over its life, so the dedupe
|
|
47
|
+
* unit is the ASK — `source_message_id` when the frame carries one (the studio reuses the
|
|
48
|
+
* SAME message id on a user-driven resend, so a retry that reached the bus twice dedupes),
|
|
49
|
+
* else the bus `event_id` (pure redelivery). Never the doc lifetime.
|
|
50
|
+
*/
|
|
51
|
+
import { InteractiveHandoffLedger } from './ledger.js';
|
|
52
|
+
import type { CoreAdapter } from '../core/adapter.js';
|
|
53
|
+
import type { WorkflowDef } from '../core/types.js';
|
|
54
|
+
export declare const CHAT_POSTED = "wicked.interactive.chat.posted";
|
|
55
|
+
/** Exact-type filter with a domain guard — no wildcard, one event type is the whole trigger. */
|
|
56
|
+
export declare const INTERACTIVE_CHAT_BUS_FILTER = "wicked.interactive.chat.posted@wicked-interactive";
|
|
57
|
+
/** Dedicated durable-cursor identity — NOT the draft/edit seams', so the three interactive
|
|
58
|
+
* seams advance independent cursors and stopping one never strands another. */
|
|
59
|
+
export declare const INTERACTIVE_CHAT_BUS_PLUGIN = "wicked-crew-interactive-chat";
|
|
60
|
+
export declare const INTERACTIVE_CHAT_WORKFLOW = "interactive-chat";
|
|
61
|
+
/**
|
|
62
|
+
* The governed workflow that fulfils an iteration ask. Two agent phases — understand (recon)
|
|
63
|
+
* then revise (build, creator role) — the draft leg's sibling: the reviser builds on a stated
|
|
64
|
+
* plan instead of one-shotting a rewrite, and the run narrates a real phase transition.
|
|
65
|
+
*
|
|
66
|
+
* The phase `instructions` carry the ITERATION contract: start from the CURRENT document (the
|
|
67
|
+
* snapshot path named in the task), change only what the ask touches, KEEP every existing
|
|
68
|
+
* `data-wid` on kept elements (interactive's instrument pass preserves pre-existing anchors —
|
|
69
|
+
* INV-1 — so feedback deep-links survive the iteration) and mint none on added ones. They are
|
|
70
|
+
* SINGLE-LINE by contract (PTY seat runner, wicked-core FINDING-011).
|
|
71
|
+
*
|
|
72
|
+
* All gates are `auto` with `validator_pin: null` — same rationale as the draft leg: the
|
|
73
|
+
* acceptance gate for a revision is interactive's instrument+theme pipeline and the user's own
|
|
74
|
+
* eyes on the canvas.
|
|
75
|
+
*/
|
|
76
|
+
export declare const INTERACTIVE_CHAT_WORKFLOW_DEF: WorkflowDef;
|
|
77
|
+
/** The chat-posted fields this seam acts on. */
|
|
78
|
+
export interface ChatAsk {
|
|
79
|
+
documentId: string;
|
|
80
|
+
/** The user's ask, verbatim (flattened later — the problem statement is single-line). */
|
|
81
|
+
text: string;
|
|
82
|
+
/** The thread message behind the ask (studio inject wire, §7.7). The studio REUSES the same
|
|
83
|
+
* id on a user-driven resend, so this is the dedupe unit when present. */
|
|
84
|
+
sourceMessageId?: string;
|
|
85
|
+
/** Present when the doc is project-bound (interactive's DES-PROJECT-001 enrichment). */
|
|
86
|
+
projectId?: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Parse a bus frame into a {@link ChatAsk}, or `null` when it is not an actionable ask:
|
|
90
|
+
* wrong type, malformed payload, non-`user` role (contract (a) — agent narration and any
|
|
91
|
+
* transcript echo ride the same topic), slug-invalid document_id, or empty text. Kind and
|
|
92
|
+
* existence checks (contract (b)) happen against the filesystem, not the payload — the frame
|
|
93
|
+
* does not carry `kind`.
|
|
94
|
+
*/
|
|
95
|
+
export declare function parseChatPosted(eventType: string, payload: unknown): ChatAsk | null;
|
|
96
|
+
/** Contract (d): `true` when the text is a conversational ask this seam should answer —
|
|
97
|
+
* not the feedback overlay's batch echo (already answered elsewhere). */
|
|
98
|
+
export declare function isIterationAsk(text: string): boolean;
|
|
99
|
+
/** The one dedupe unit of this seam: the ASK — the studio's message id when the frame carries
|
|
100
|
+
* one (a resend reuses it), else the bus event id (pure redelivery). Never the doc lifetime. */
|
|
101
|
+
export declare function chatKey(documentId: string, eventId: number, sourceMessageId?: string): string;
|
|
102
|
+
/** Deterministic bus idempotency key for the one revision this seam may land per ask. */
|
|
103
|
+
export declare function chatIdempotencyKey(documentId: string, eventId: number, sourceMessageId?: string): string;
|
|
104
|
+
/** What the versions.json read yields — enough to gate (kind) and snapshot (head html path). */
|
|
105
|
+
export interface DocHead {
|
|
106
|
+
kind: string;
|
|
107
|
+
head: number;
|
|
108
|
+
/** Absolute path of the head version's html artifact. */
|
|
109
|
+
headHtmlPath: string;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Read a doc workspace's manifest (interactive's `versions.json`, fsstore.js shape) and resolve
|
|
113
|
+
* its head html path. `null` when the doc does not exist under this root or the manifest is
|
|
114
|
+
* unreadable — not ours to answer, per contract (b). Follows interactive's own tolerant read:
|
|
115
|
+
* `kind` defaults to `"doc"` when absent (listDocs does the same) — and ABSENT is what a real
|
|
116
|
+
* source doc looks like on disk (interactive records kind only for demo docs), so the caller
|
|
117
|
+
* gates through {@link isAnswerableDocKind}, never a `=== 'source'` comparison.
|
|
118
|
+
*/
|
|
119
|
+
export declare function readDocHead(docsRoot: string, documentId: string): DocHead | null;
|
|
120
|
+
/**
|
|
121
|
+
* Contract (b)'s kind half: `true` when a doc of this manifest kind is this seam's to answer.
|
|
122
|
+
* `source` is the spec's spelling (recorded by no released interactive yet, accepted for the
|
|
123
|
+
* day a manifest carries it); `doc` is the absent-default every REAL source (and plain html)
|
|
124
|
+
* doc reads as — interactive's initManifest keeps non-demo kinds implicit. Explicit foreign
|
|
125
|
+
* kinds (`demo`, anything future) belong to their own loops.
|
|
126
|
+
*/
|
|
127
|
+
export declare function isAnswerableDocKind(kind: string): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* The run's problem statement (the engine scopes it per phase and folds each phase's
|
|
130
|
+
* instructions on top). Carries everything ask-specific: identity, the flattened ask, the
|
|
131
|
+
* CURRENT-version snapshot to read, and the absolute path the revised HTML must land at.
|
|
132
|
+
*
|
|
133
|
+
* CREW-UX-8 SPLIT — the revision leg carries NO repo grounding, deliberately. The draft leg
|
|
134
|
+
* (draft-events.ts) grounds its v1 in a launch-scoped repo snapshot and that shape is proven
|
|
135
|
+
* live; the SAME shape on the revise turn wedged 2/2 on the real engine (crew#288 comment —
|
|
136
|
+
* the repo-grounded revise turn hits the known second-turn wedge), while UNGROUNDED revisions
|
|
137
|
+
* are proven to land (the CREW-UX-5 verification). So bound asks launch exactly like unfiled
|
|
138
|
+
* ones — head-copy in the external inbox, one write root, no repoRef (wicked-core#293), no
|
|
139
|
+
* live-repo path in the task (wicked-core#294). Revision grounding returns when either of
|
|
140
|
+
* those is fixed.
|
|
141
|
+
*/
|
|
142
|
+
export declare function chatProblem(ask: ChatAsk, currentPath: string, outPath: string): string;
|
|
143
|
+
/** Options for {@link startInteractiveChatSubscriber}. */
|
|
144
|
+
export interface InteractiveChatOptions {
|
|
145
|
+
/** Bus SQLite db path. Omit to let wicked-bus resolve its own default
|
|
146
|
+
* (honors `WICKED_BUS_DATA_DIR`) — where interactive's service emits unless redirected. */
|
|
147
|
+
dbPath?: string;
|
|
148
|
+
/** Poll cadence, ms (default 2000; tests shorten it). */
|
|
149
|
+
pollIntervalMs?: number;
|
|
150
|
+
/** Heartbeat narration cadence while a run is in flight, ms (default 15000 — inside the
|
|
151
|
+
* UI's ~20s `status.requested` window so the canvas never reads frozen; the studio's own
|
|
152
|
+
* 90s silence budget makes the FIRST pickup narration the load-bearing one). */
|
|
153
|
+
heartbeatMs?: number;
|
|
154
|
+
/** Ledger file (default `~/.wicked-crew/interactive-chat-ledger.json`). */
|
|
155
|
+
ledgerPath?: string;
|
|
156
|
+
/** Root under which each ask gets its own per-run subdirectory (`<chatDir>/<safeKey>/`)
|
|
157
|
+
* holding the head snapshot and the revised deliverable; only that subdirectory is declared
|
|
158
|
+
* as the run's extra write root (per-run isolation — Copilot, crew#313: declaring the shared
|
|
159
|
+
* dir wholesale let one doc's worker read every other doc's head and revision).
|
|
160
|
+
* Default `~/.wicked-crew/interactive-chats`. */
|
|
161
|
+
chatDir?: string;
|
|
162
|
+
/** Seat roster JSON for the governed run (default: the production council roster).
|
|
163
|
+
* The functional-test harness passes a deterministic stub seat here. */
|
|
164
|
+
clisJson?: string;
|
|
165
|
+
/** The docs root an ask's doc is read from. Default: the shared-default resolution
|
|
166
|
+
* (`WICKED_INTERACTIVE_ROOT` › `~/wicked-interactive/docs`); the server wires the
|
|
167
|
+
* per-project `interactiveRoot` setting through here so a project on its own root
|
|
168
|
+
* resolves correctly. */
|
|
169
|
+
resolveDocsRoot?: (projectId: string | undefined) => string;
|
|
170
|
+
/** Contract (c)'s cross-seam half: `true` while a DRAFT or EDIT run is in flight for the
|
|
171
|
+
* doc. The server wires the sibling subscriptions' in-flight sets through here; the chat
|
|
172
|
+
* seam's own in-flight runs are tracked internally. */
|
|
173
|
+
isDocBusy?: (documentId: string) => boolean;
|
|
174
|
+
/** Queue-drain sweep cadence, ms (default 1000; tests shorten it). */
|
|
175
|
+
queueSweepMs?: number;
|
|
176
|
+
/** How long a drained ask waits for the PREVIOUS revision's version to land before
|
|
177
|
+
* proceeding on the stale head anyway, ms (default 60000; tests shorten it). */
|
|
178
|
+
landingGateMs?: number;
|
|
179
|
+
/** Called after a launch that FILED the run into a project (chat.posted carried
|
|
180
|
+
* `project_id`). Same wiring as the sibling seams: the server points this at the launch
|
|
181
|
+
* route's post-commit half (membership index tag + membership.attached emit). */
|
|
182
|
+
onRunFiled?: (runId: string, projectId: string) => void;
|
|
183
|
+
/** Diagnostics sink (default: console.error). */
|
|
184
|
+
log?: (message: string) => void;
|
|
185
|
+
}
|
|
186
|
+
/** Handle for a running subscription. */
|
|
187
|
+
export interface InteractiveChatSubscription {
|
|
188
|
+
stop(): Promise<void> | void;
|
|
189
|
+
/** The durable ledger (diagnostics / tests). */
|
|
190
|
+
ledger: InteractiveHandoffLedger;
|
|
191
|
+
/** Documents with a chat run currently in flight (diagnostics / cross-seam wiring). */
|
|
192
|
+
inFlightDocs(): string[];
|
|
193
|
+
/** Asks queued behind a busy doc, per doc (diagnostics / tests). */
|
|
194
|
+
queuedCount(documentId: string): number;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Arm the seam: register the `interactive-chat` workflow, open a durable
|
|
198
|
+
* `wicked.interactive.chat.posted` subscription, and answer each user ask on an existing
|
|
199
|
+
* answerable doc (contract (b)) with a governed run that ends in `wicked.interactive.draft.completed`
|
|
200
|
+
* (the service lands the revised full HTML as a generated version).
|
|
201
|
+
*
|
|
202
|
+
* Graceful degradation mirrors the sibling seams: a missing wicked-bus package or an
|
|
203
|
+
* unopenable db LOGS and returns `null` — the daemon must still boot on a machine whose bus
|
|
204
|
+
* is broken.
|
|
205
|
+
*/
|
|
206
|
+
export declare function startInteractiveChatSubscriber(adapter: CoreAdapter, opts?: InteractiveChatOptions): Promise<InteractiveChatSubscription | null>;
|
|
207
|
+
//# sourceMappingURL=chat-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-events.d.ts","sourceRoot":"","sources":["../../src/interactive/chat-events.ts"],"names":[],"mappings":"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"}
|