wicked-crew 0.7.40 → 0.7.45
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 +3 -0
- package/dist/api/audit.d.ts.map +1 -1
- package/dist/api/audit.js +2 -0
- package/dist/api/audit.js.map +1 -1
- package/dist/api/capture.d.ts +149 -0
- package/dist/api/capture.d.ts.map +1 -0
- package/dist/api/capture.js +330 -0
- package/dist/api/capture.js.map +1 -0
- package/dist/api/chat-citations.d.ts +174 -0
- package/dist/api/chat-citations.d.ts.map +1 -0
- package/dist/api/chat-citations.js +528 -0
- package/dist/api/chat-citations.js.map +1 -0
- package/dist/api/chat-transcripts.d.ts +20 -1
- package/dist/api/chat-transcripts.d.ts.map +1 -1
- package/dist/api/chat-transcripts.js +29 -0
- package/dist/api/chat-transcripts.js.map +1 -1
- package/dist/api/delivery-cache.d.ts +2 -2
- package/dist/api/delivery-freeze.d.ts +66 -0
- package/dist/api/delivery-freeze.d.ts.map +1 -0
- package/dist/api/delivery-freeze.js +104 -0
- package/dist/api/delivery-freeze.js.map +1 -0
- package/dist/api/delivery-index.d.ts +53 -9
- package/dist/api/delivery-index.d.ts.map +1 -1
- package/dist/api/delivery-index.js +115 -22
- package/dist/api/delivery-index.js.map +1 -1
- package/dist/api/demo.d.ts +90 -0
- package/dist/api/demo.d.ts.map +1 -0
- package/dist/api/demo.js +492 -0
- package/dist/api/demo.js.map +1 -0
- package/dist/api/diagnostics.d.ts +64 -1
- package/dist/api/diagnostics.d.ts.map +1 -1
- package/dist/api/diagnostics.js +129 -1
- package/dist/api/diagnostics.js.map +1 -1
- package/dist/api/eval-sample.d.ts +6 -6
- package/dist/api/governance-steering.d.ts +2 -2
- package/dist/api/mcp.d.ts +282 -0
- package/dist/api/mcp.d.ts.map +1 -0
- package/dist/api/mcp.js +521 -0
- package/dist/api/mcp.js.map +1 -0
- package/dist/api/plan-schema.d.ts +56 -0
- package/dist/api/plan-schema.d.ts.map +1 -0
- package/dist/api/plan-schema.js +26 -0
- package/dist/api/plan-schema.js.map +1 -0
- package/dist/api/roster-standing.d.ts +15 -2
- package/dist/api/roster-standing.d.ts.map +1 -1
- package/dist/api/roster-standing.js +55 -3
- package/dist/api/roster-standing.js.map +1 -1
- package/dist/api/routes.d.ts +630 -51
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +738 -127
- package/dist/api/routes.js.map +1 -1
- package/dist/api/run-files.d.ts.map +1 -1
- package/dist/api/run-files.js +31 -2
- package/dist/api/run-files.js.map +1 -1
- package/dist/api/run-liveness.d.ts +41 -0
- package/dist/api/run-liveness.d.ts.map +1 -0
- package/dist/api/run-liveness.js +166 -0
- package/dist/api/run-liveness.js.map +1 -0
- package/dist/api/seat-health.d.ts.map +1 -1
- package/dist/api/seat-health.js +20 -4
- package/dist/api/seat-health.js.map +1 -1
- package/dist/api/seat-probe.d.ts +141 -0
- package/dist/api/seat-probe.d.ts.map +1 -0
- package/dist/api/seat-probe.js +257 -0
- package/dist/api/seat-probe.js.map +1 -0
- package/dist/api/seat-record.d.ts +40 -0
- package/dist/api/seat-record.d.ts.map +1 -0
- package/dist/api/seat-record.js +218 -0
- package/dist/api/seat-record.js.map +1 -0
- package/dist/api/seat-signin.d.ts.map +1 -1
- package/dist/api/seat-signin.js +3 -1
- package/dist/api/seat-signin.js.map +1 -1
- package/dist/api/seat-standing.d.ts +24 -4
- package/dist/api/seat-standing.d.ts.map +1 -1
- package/dist/api/seat-standing.js +50 -3
- package/dist/api/seat-standing.js.map +1 -1
- package/dist/api/server.d.ts +34 -48
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +283 -107
- package/dist/api/server.js.map +1 -1
- package/dist/api/skills.d.ts +3 -1
- package/dist/api/skills.d.ts.map +1 -1
- package/dist/api/skills.js +7 -2
- package/dist/api/skills.js.map +1 -1
- package/dist/api/stall-watchdog.d.ts +34 -3
- package/dist/api/stall-watchdog.d.ts.map +1 -1
- package/dist/api/stall-watchdog.js +107 -8
- package/dist/api/stall-watchdog.js.map +1 -1
- package/dist/api/steering-landing.d.ts +6 -6
- package/dist/api/steering-landing.d.ts.map +1 -1
- package/dist/api/steering-landing.js +7 -9
- package/dist/api/steering-landing.js.map +1 -1
- package/dist/api/testing.d.ts +6 -4
- package/dist/api/testing.d.ts.map +1 -1
- package/dist/api/testing.js +82 -47
- package/dist/api/testing.js.map +1 -1
- package/dist/campaign/boot-resume.d.ts +34 -0
- package/dist/campaign/boot-resume.d.ts.map +1 -0
- package/dist/campaign/boot-resume.js +45 -0
- package/dist/campaign/boot-resume.js.map +1 -0
- package/dist/campaigns/rollup.d.ts +4 -1
- package/dist/campaigns/rollup.d.ts.map +1 -1
- package/dist/campaigns/rollup.js +6 -1
- package/dist/campaigns/rollup.js.map +1 -1
- package/dist/campaigns/routes.d.ts +2 -2
- package/dist/cli/bearer.d.ts +27 -1
- package/dist/cli/bearer.d.ts.map +1 -1
- package/dist/cli/bearer.js +62 -2
- package/dist/cli/bearer.js.map +1 -1
- package/dist/cli/governance.d.ts +11 -0
- package/dist/cli/governance.d.ts.map +1 -1
- package/dist/cli/governance.js +8 -2
- package/dist/cli/governance.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +140 -75
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.d.ts +11 -0
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +40 -9
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/service.d.ts +78 -0
- package/dist/cli/service.d.ts.map +1 -0
- package/dist/cli/service.js +334 -0
- package/dist/cli/service.js.map +1 -0
- package/dist/core/adapter.d.ts +238 -12
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +619 -157
- package/dist/core/adapter.js.map +1 -1
- package/dist/core/bridge-reaper.d.ts +4 -1
- package/dist/core/bridge-reaper.d.ts.map +1 -1
- package/dist/core/bridge-reaper.js +15 -1
- package/dist/core/bridge-reaper.js.map +1 -1
- package/dist/core/bus-notice.d.ts +27 -0
- package/dist/core/bus-notice.d.ts.map +1 -0
- package/dist/core/bus-notice.js +24 -0
- package/dist/core/bus-notice.js.map +1 -0
- package/dist/core/bus.d.ts +99 -0
- package/dist/core/bus.d.ts.map +1 -0
- package/dist/core/bus.js +176 -0
- package/dist/core/bus.js.map +1 -0
- package/dist/core/deliver-exclusions.d.ts +71 -0
- package/dist/core/deliver-exclusions.d.ts.map +1 -0
- package/dist/core/deliver-exclusions.js +110 -0
- package/dist/core/deliver-exclusions.js.map +1 -0
- package/dist/core/deliver-text.d.ts +61 -14
- package/dist/core/deliver-text.d.ts.map +1 -1
- package/dist/core/deliver-text.js +178 -30
- package/dist/core/deliver-text.js.map +1 -1
- package/dist/core/deliver-triage.d.ts +11 -2
- package/dist/core/deliver-triage.d.ts.map +1 -1
- package/dist/core/deliver-triage.js +19 -1
- package/dist/core/deliver-triage.js.map +1 -1
- package/dist/core/deliver.d.ts +161 -3
- package/dist/core/deliver.d.ts.map +1 -1
- package/dist/core/deliver.js +463 -25
- package/dist/core/deliver.js.map +1 -1
- package/dist/core/deliverable-floor.d.ts.map +1 -1
- package/dist/core/deliverable-floor.js +5 -7
- package/dist/core/deliverable-floor.js.map +1 -1
- package/dist/core/engine-bus.d.ts +40 -0
- package/dist/core/engine-bus.d.ts.map +1 -0
- package/dist/core/engine-bus.js +27 -0
- package/dist/core/engine-bus.js.map +1 -0
- package/dist/core/estate-mcp-client.d.ts +87 -5
- package/dist/core/estate-mcp-client.d.ts.map +1 -1
- package/dist/core/estate-mcp-client.js +90 -11
- package/dist/core/estate-mcp-client.js.map +1 -1
- package/dist/core/linked-issues.d.ts +46 -0
- package/dist/core/linked-issues.d.ts.map +1 -0
- package/dist/core/linked-issues.js +123 -0
- package/dist/core/linked-issues.js.map +1 -0
- package/dist/core/run-identity.d.ts +100 -0
- package/dist/core/run-identity.d.ts.map +1 -0
- package/dist/core/run-identity.js +178 -0
- package/dist/core/run-identity.js.map +1 -0
- package/dist/core/shutdown.d.ts +16 -0
- package/dist/core/shutdown.d.ts.map +1 -0
- package/dist/core/shutdown.js +39 -0
- package/dist/core/shutdown.js.map +1 -0
- package/dist/core/types.d.ts +15 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +4 -0
- package/dist/core/types.js.map +1 -1
- package/dist/interactive/bridge-pool.d.ts +67 -3
- package/dist/interactive/bridge-pool.d.ts.map +1 -1
- package/dist/interactive/bridge-pool.js +129 -13
- package/dist/interactive/bridge-pool.js.map +1 -1
- package/dist/interactive/bridge-root.d.ts +9 -0
- package/dist/interactive/bridge-root.d.ts.map +1 -1
- package/dist/interactive/bridge-root.js +11 -4
- package/dist/interactive/bridge-root.js.map +1 -1
- package/dist/interactive/chat-events.d.ts +14 -9
- package/dist/interactive/chat-events.d.ts.map +1 -1
- package/dist/interactive/chat-events.js +55 -57
- package/dist/interactive/chat-events.js.map +1 -1
- package/dist/interactive/council-outcome.d.ts +1 -0
- package/dist/interactive/council-outcome.d.ts.map +1 -1
- package/dist/interactive/council-outcome.js +33 -0
- package/dist/interactive/council-outcome.js.map +1 -1
- package/dist/interactive/draft-events.d.ts +36 -17
- package/dist/interactive/draft-events.d.ts.map +1 -1
- package/dist/interactive/draft-events.js +121 -92
- package/dist/interactive/draft-events.js.map +1 -1
- package/dist/interactive/draft-skill.d.ts +59 -0
- package/dist/interactive/draft-skill.d.ts.map +1 -1
- package/dist/interactive/draft-skill.js +233 -0
- package/dist/interactive/draft-skill.js.map +1 -1
- package/dist/interactive/edit-events.d.ts +1 -14
- package/dist/interactive/edit-events.d.ts.map +1 -1
- package/dist/interactive/edit-events.js +37 -70
- package/dist/interactive/edit-events.js.map +1 -1
- package/dist/interactive/proxy-routes.d.ts +2 -2
- package/dist/interactive/proxy-routes.d.ts.map +1 -1
- package/dist/interactive/proxy-routes.js +4 -13
- package/dist/interactive/proxy-routes.js.map +1 -1
- package/dist/interactive/ws-relay.d.ts +9 -10
- package/dist/interactive/ws-relay.d.ts.map +1 -1
- package/dist/interactive/ws-relay.js +23 -50
- package/dist/interactive/ws-relay.js.map +1 -1
- package/dist/mcp/broker.d.ts +88 -0
- package/dist/mcp/broker.d.ts.map +1 -0
- package/dist/mcp/broker.js +307 -0
- package/dist/mcp/broker.js.map +1 -0
- package/dist/mcp/call-records.d.ts +61 -0
- package/dist/mcp/call-records.d.ts.map +1 -0
- package/dist/mcp/call-records.js +135 -0
- package/dist/mcp/call-records.js.map +1 -0
- package/dist/mcp/classify.d.ts +45 -0
- package/dist/mcp/classify.d.ts.map +1 -0
- package/dist/mcp/classify.js +83 -0
- package/dist/mcp/classify.js.map +1 -0
- package/dist/mcp/discovery.d.ts +24 -0
- package/dist/mcp/discovery.d.ts.map +1 -0
- package/dist/mcp/discovery.js +78 -0
- package/dist/mcp/discovery.js.map +1 -0
- package/dist/mcp/invoke.d.ts +36 -0
- package/dist/mcp/invoke.d.ts.map +1 -0
- package/dist/mcp/invoke.js +80 -0
- package/dist/mcp/invoke.js.map +1 -0
- package/dist/mcp/policies.d.ts +110 -0
- package/dist/mcp/policies.d.ts.map +1 -0
- package/dist/mcp/policies.js +400 -0
- package/dist/mcp/policies.js.map +1 -0
- package/dist/mcp/probe.d.ts +66 -0
- package/dist/mcp/probe.d.ts.map +1 -0
- package/dist/mcp/probe.js +116 -0
- package/dist/mcp/probe.js.map +1 -0
- package/dist/mcp/redact.d.ts +34 -0
- package/dist/mcp/redact.d.ts.map +1 -0
- package/dist/mcp/redact.js +84 -0
- package/dist/mcp/redact.js.map +1 -0
- package/dist/mcp/registry-store.d.ts +69 -0
- package/dist/mcp/registry-store.d.ts.map +1 -0
- package/dist/mcp/registry-store.js +82 -0
- package/dist/mcp/registry-store.js.map +1 -0
- package/dist/mcp/registry.d.ts +147 -0
- package/dist/mcp/registry.d.ts.map +1 -0
- package/dist/mcp/registry.js +509 -0
- package/dist/mcp/registry.js.map +1 -0
- package/dist/mcp/rest.d.ts +111 -0
- package/dist/mcp/rest.d.ts.map +1 -0
- package/dist/mcp/rest.js +606 -0
- package/dist/mcp/rest.js.map +1 -0
- package/dist/mcp/secrets.d.ts +66 -0
- package/dist/mcp/secrets.d.ts.map +1 -0
- package/dist/mcp/secrets.js +180 -0
- package/dist/mcp/secrets.js.map +1 -0
- package/dist/mcp/tool-list.d.ts +25 -0
- package/dist/mcp/tool-list.d.ts.map +1 -0
- package/dist/mcp/tool-list.js +62 -0
- package/dist/mcp/tool-list.js.map +1 -0
- package/dist/mcp/upstream-error.d.ts +12 -0
- package/dist/mcp/upstream-error.d.ts.map +1 -0
- package/dist/mcp/upstream-error.js +14 -0
- package/dist/mcp/upstream-error.js.map +1 -0
- package/dist/mcp/usage.d.ts +31 -0
- package/dist/mcp/usage.d.ts.map +1 -0
- package/dist/mcp/usage.js +159 -0
- package/dist/mcp/usage.js.map +1 -0
- package/dist/presets/routes.d.ts +53 -0
- package/dist/presets/routes.d.ts.map +1 -0
- package/dist/presets/routes.js +115 -0
- package/dist/presets/routes.js.map +1 -0
- package/dist/projects/activity.d.ts +6 -18
- package/dist/projects/activity.d.ts.map +1 -1
- package/dist/projects/activity.js +17 -60
- package/dist/projects/activity.js.map +1 -1
- package/dist/projects/events.d.ts +6 -5
- package/dist/projects/events.d.ts.map +1 -1
- package/dist/projects/events.js +33 -58
- package/dist/projects/events.js.map +1 -1
- package/dist/projects/routes.d.ts.map +1 -1
- package/dist/projects/routes.js +19 -0
- package/dist/projects/routes.js.map +1 -1
- package/dist/projects/state-home-preflight.d.ts.map +1 -1
- package/dist/projects/state-home-preflight.js +7 -0
- package/dist/projects/state-home-preflight.js.map +1 -1
- package/dist/projects/state-home-registry.d.ts.map +1 -1
- package/dist/projects/state-home-registry.js +9 -1
- package/dist/projects/state-home-registry.js.map +1 -1
- package/dist/qe/acceptance.d.ts +28 -28
- package/dist/qe/acceptance.d.ts.map +1 -1
- package/dist/qe/acceptance.js +60 -61
- package/dist/qe/acceptance.js.map +1 -1
- package/dist/qe/test-sets.d.ts +3 -5
- package/dist/qe/test-sets.d.ts.map +1 -1
- package/dist/qe/test-sets.js +5 -7
- package/dist/qe/test-sets.js.map +1 -1
- package/dist/skills/installed.d.ts +51 -0
- package/dist/skills/installed.d.ts.map +1 -0
- package/dist/skills/installed.js +46 -0
- package/dist/skills/installed.js.map +1 -0
- package/dist/standing-orders/evaluator.d.ts +113 -0
- package/dist/standing-orders/evaluator.d.ts.map +1 -0
- package/dist/standing-orders/evaluator.js +225 -0
- package/dist/standing-orders/evaluator.js.map +1 -0
- package/dist/standing-orders/history.d.ts +58 -0
- package/dist/standing-orders/history.d.ts.map +1 -0
- package/dist/standing-orders/history.js +117 -0
- package/dist/standing-orders/history.js.map +1 -0
- package/dist/standing-orders/parse.d.ts +19 -0
- package/dist/standing-orders/parse.d.ts.map +1 -0
- package/dist/standing-orders/parse.js +75 -0
- package/dist/standing-orders/parse.js.map +1 -0
- package/dist/standing-orders/routes.d.ts +180 -0
- package/dist/standing-orders/routes.d.ts.map +1 -0
- package/dist/standing-orders/routes.js +95 -0
- package/dist/standing-orders/routes.js.map +1 -0
- package/dist/standing-orders/store.d.ts +154 -0
- package/dist/standing-orders/store.d.ts.map +1 -0
- package/dist/standing-orders/store.js +189 -0
- package/dist/standing-orders/store.js.map +1 -0
- package/dist/studio/assets/index-CvV5qITf.css +32 -0
- package/dist/studio/assets/index-jCOy6Ns7.js +568 -0
- package/dist/studio/index.html +3 -3
- package/dist/studio/testid-inventory.json +3274 -894
- package/dist/team/key.d.ts +5 -0
- package/dist/team/key.d.ts.map +1 -0
- package/dist/team/key.js +21 -0
- package/dist/team/key.js.map +1 -0
- package/dist/team/routes.d.ts +196 -0
- package/dist/team/routes.d.ts.map +1 -0
- package/dist/team/routes.js +274 -0
- package/dist/team/routes.js.map +1 -0
- package/dist/team/ws-relay.d.ts +39 -0
- package/dist/team/ws-relay.d.ts.map +1 -0
- package/dist/team/ws-relay.js +63 -0
- package/dist/team/ws-relay.js.map +1 -0
- package/endpoint-manifest.json +502 -6
- package/package.json +6 -6
- package/dist/interactive/demo-events.d.ts +0 -367
- package/dist/interactive/demo-events.d.ts.map +0 -1
- package/dist/interactive/demo-events.js +0 -1293
- package/dist/interactive/demo-events.js.map +0 -1
- package/dist/qe/gate-events.d.ts +0 -113
- package/dist/qe/gate-events.d.ts.map +0 -1
- package/dist/qe/gate-events.js +0 -172
- package/dist/qe/gate-events.js.map +0 -1
- package/dist/studio/assets/index-Bg7Z3BR-.css +0 -32
- package/dist/studio/assets/index-BvOFMjf9.js +0 -554
package/dist/api/server.js
CHANGED
|
@@ -2,7 +2,7 @@ import Fastify from 'fastify';
|
|
|
2
2
|
import fastifyWebsocket from '@fastify/websocket';
|
|
3
3
|
import fastifyStatic from '@fastify/static';
|
|
4
4
|
import { existsSync } from 'node:fs';
|
|
5
|
-
import { join } from 'node:path';
|
|
5
|
+
import { join, resolve as resolvePath } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { registerRoutes } from './routes.js';
|
|
8
8
|
import { ErrorRing, teeStreamWithErrorRing } from './diagnostics.js';
|
|
@@ -16,17 +16,17 @@ import { GroupIndex } from './group-index.js';
|
|
|
16
16
|
import { RunTimingIndex, recordRunLaunched } from './run-timing-index.js';
|
|
17
17
|
import { GuidanceIndex } from './guidance-index.js';
|
|
18
18
|
import { ChatScopeIndex, reapStaleChatNamespaces } from './chat-scope.js';
|
|
19
|
-
import {
|
|
19
|
+
import { chatCitationDeps, citationsFrame, verifyCitations, } from './chat-citations.js';
|
|
20
|
+
import { DeliveryIndex, deliverUnitOf, gitRunBranchIsEmpty, gitWorktreeIsClean, deliveryRecordFrom, canDeliverResolver, } from './delivery-index.js';
|
|
21
|
+
import { DeliveryFreeze } from './delivery-freeze.js';
|
|
20
22
|
import { DeliveryDerivationCache } from './delivery-cache.js';
|
|
21
23
|
import { coreUnitId } from './evidence.js';
|
|
22
24
|
import { registerClient, broadcast } from '../events/bus.js';
|
|
23
25
|
import { TerminalHub, registerTerminalWs } from '../events/terminals.js';
|
|
24
|
-
import { QeGateCache, startQeGateSubscriber } from '../qe/gate-events.js';
|
|
25
26
|
import { INTERACTIVE_DRAFT_WORKFLOW_DEF, startInteractiveDraftSubscriber } from '../interactive/draft-events.js';
|
|
26
27
|
import { INTERACTIVE_EDIT_WORKFLOW_DEF, startInteractiveEditSubscriber } from '../interactive/edit-events.js';
|
|
27
28
|
import { INTERACTIVE_CHAT_WORKFLOW_DEF, startInteractiveChatSubscriber } from '../interactive/chat-events.js';
|
|
28
29
|
import { PhaseSkillArming, RunSkillGapIndex } from '../skills/phase-skill-gaps.js';
|
|
29
|
-
import { startInteractiveDemoSubscriber } from '../interactive/demo-events.js';
|
|
30
30
|
import { resolveProjectInteractiveRoot } from '../interactive/bridge-root.js';
|
|
31
31
|
import { sweepDocLedgers } from '../interactive/doc-ledger-sweep.js';
|
|
32
32
|
import { DocRunIndex } from '../interactive/doc-run-index.js';
|
|
@@ -34,8 +34,18 @@ import { TestSetIndex, registerTestSetForRun } from '../qe/test-sets.js';
|
|
|
34
34
|
import { DocGroundingStore } from '../interactive/doc-grounding.js';
|
|
35
35
|
import { ProjectSettingsStore } from '../projects/settings.js';
|
|
36
36
|
import { crewStateHome } from '../projects/state-home.js';
|
|
37
|
+
import { homedir } from 'node:os';
|
|
38
|
+
import { discoverMcpServers } from '../mcp/discovery.js';
|
|
39
|
+
import { probeMcpServer } from '../mcp/probe.js';
|
|
40
|
+
import { budgetFromEnv, MCP_CALL_COMPLETED, McpBroker } from '../mcp/broker.js';
|
|
41
|
+
import { McpCallRecordFile } from '../mcp/call-records.js';
|
|
42
|
+
import { emitOnBus } from '../core/bus.js';
|
|
43
|
+
import { McpRegistry } from '../mcp/registry.js';
|
|
44
|
+
import { McpRegistryStore } from '../mcp/registry-store.js';
|
|
45
|
+
import { platformSecretStore } from '../mcp/secrets.js';
|
|
37
46
|
import { startProjectBus, MEMBERSHIP_ATTACHED, membershipAttachedKey } from '../projects/events.js';
|
|
38
47
|
import { startInteractiveWsRelay, registerInteractiveEventRoutes } from '../interactive/ws-relay.js';
|
|
48
|
+
import { startTeamWsRelay } from '../team/ws-relay.js';
|
|
39
49
|
import { MembershipIndex } from '../projects/membership-index.js';
|
|
40
50
|
import { writeRunEvidencePointer } from '../projects/charter.js';
|
|
41
51
|
import { engineBenchesUnclassifiedSeats, CoreAdapter } from '../core/adapter.js';
|
|
@@ -48,6 +58,15 @@ import { ChatTranscriptStore } from './chat-transcripts.js';
|
|
|
48
58
|
import { sweepDeliveredWorktree } from './worktree-sweep.js';
|
|
49
59
|
import { installEndpointManifestHook } from './endpoint-manifest.js';
|
|
50
60
|
import { WorkerStallWatchdog } from './stall-watchdog.js';
|
|
61
|
+
import { RunLivenessSampler } from './run-liveness.js';
|
|
62
|
+
import { resumeRunningCampaigns } from '../campaign/boot-resume.js';
|
|
63
|
+
import { StandingOrderStore } from '../standing-orders/store.js';
|
|
64
|
+
import { StandingOrderEvaluator } from '../standing-orders/evaluator.js';
|
|
65
|
+
import { registerStandingOrderRoutes } from '../standing-orders/routes.js';
|
|
66
|
+
import { seatParser } from '../standing-orders/parse.js';
|
|
67
|
+
import { registerGateHistoryRoute, runBand, runPreset, runProject } from '../standing-orders/history.js';
|
|
68
|
+
import { busRows, openPlanGateRisk } from '../team/routes.js';
|
|
69
|
+
import { isSteeringAuthorRun } from './steering-landing.js';
|
|
51
70
|
import { applyWorkerConfigRoot } from './seat-signin.js';
|
|
52
71
|
import { registeredSkillRefs } from '../skills/core-closure.js';
|
|
53
72
|
import { assertSkillsRootFenced } from '../skills/root-fence.js';
|
|
@@ -95,9 +114,11 @@ export async function createServer(adapter, options) {
|
|
|
95
114
|
// test); costs one array push per route at boot, nothing per request.
|
|
96
115
|
app.decorate('endpointManifest', installEndpointManifestHook(app));
|
|
97
116
|
const gateCache = new GateCache();
|
|
117
|
+
// Standing orders (behaviour 10): armed once the routes (and so THE gate decision path) exist;
|
|
118
|
+
// the event relays above it read it late-bound.
|
|
119
|
+
let standingOrderEvaluator = null;
|
|
98
120
|
const elicitationCache = new ElicitationCache();
|
|
99
121
|
const terminals = new TerminalHub();
|
|
100
|
-
const qeGateCache = new QeGateCache();
|
|
101
122
|
// Per-seat runtime health (crew#274): folded from the single CoreEvent subscription below,
|
|
102
123
|
// surfaced on GET /roster, recovered by the low-frequency probe armed further down.
|
|
103
124
|
const seatHealth = new SeatHealthTracker({
|
|
@@ -311,6 +332,10 @@ export async function createServer(adapter, options) {
|
|
|
311
332
|
// trail's `run.delivered` entries so `session.delivery` survives a daemon restart.
|
|
312
333
|
const deliveryIndex = new DeliveryIndex();
|
|
313
334
|
await deliveryIndex.hydrate(audit, (m) => app.log.warn(m));
|
|
335
|
+
// Freeze deliveries (idea 15): same durable pattern — the trail's `deliveries.frozen` /
|
|
336
|
+
// `deliveries.unfrozen` entries, so a restart keeps the freeze on.
|
|
337
|
+
const deliveryFreeze = new DeliveryFreeze();
|
|
338
|
+
await deliveryFreeze.hydrate(audit, (m) => app.log.warn(m));
|
|
314
339
|
// Wave 6 (F-7R2-014): the test sets `qe-author-tests` runs registered — same durable pattern,
|
|
315
340
|
// hydrated from the trail's `testing.testset.registered` entries, fed at each terminal frame.
|
|
316
341
|
const testSets = new TestSetIndex();
|
|
@@ -337,7 +362,7 @@ export async function createServer(adapter, options) {
|
|
|
337
362
|
const deliveryCache = new DeliveryDerivationCache({
|
|
338
363
|
listViews: () => adapter.sessionsDetail(),
|
|
339
364
|
probes: vacuityProbes,
|
|
340
|
-
isDelivered: (runId) => deliveryIndex.
|
|
365
|
+
isDelivered: (runId) => deliveryIndex.isDelivered(runId),
|
|
341
366
|
canDeliver,
|
|
342
367
|
log: (m) => app.log.warn(m),
|
|
343
368
|
// Non-probe derivation throws are defects — error level, so the diagnostics ring sees them.
|
|
@@ -365,7 +390,7 @@ export async function createServer(adapter, options) {
|
|
|
365
390
|
try {
|
|
366
391
|
// Resume/retry re-terminals: already resolved once, and a terminal run's PR URL never
|
|
367
392
|
// changes — never re-read, never double-write the trail.
|
|
368
|
-
if (deliveryIndex.
|
|
393
|
+
if (deliveryIndex.isDelivered(runId))
|
|
369
394
|
return;
|
|
370
395
|
const views = await adapter.sessionsDetail();
|
|
371
396
|
const view = views.find((v) => v.session.id === runId);
|
|
@@ -380,28 +405,35 @@ export async function createServer(adapter, options) {
|
|
|
380
405
|
const output = await adapter.workOutput(coreUnitId(runId, unit));
|
|
381
406
|
if (output === null)
|
|
382
407
|
return;
|
|
383
|
-
|
|
384
|
-
|
|
408
|
+
// A PR URL, or (N1) a push-only delivery to an origin gh cannot resolve to GitHub — a
|
|
409
|
+
// delivery too, so the run reads `delivery: 'pushed'` and never `'stranded'`.
|
|
410
|
+
const record = deliveryRecordFrom(output);
|
|
411
|
+
if (record === null)
|
|
385
412
|
return;
|
|
386
413
|
// The durable record first, then the read-side index — the same write order as
|
|
387
414
|
// `guidance.set`, so the index can only LAG a crash (rehydrated at next boot), never
|
|
388
415
|
// hold a record the trail does not.
|
|
389
|
-
audit.record('run.delivered', DAEMON_ACTOR, {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
416
|
+
audit.record('run.delivered', DAEMON_ACTOR, { runId, detail: record });
|
|
417
|
+
if ('url' in record)
|
|
418
|
+
deliveryIndex.set(runId, record.url);
|
|
419
|
+
else
|
|
420
|
+
deliveryIndex.setPushed(runId, record.pushed);
|
|
394
421
|
}
|
|
395
422
|
catch (err) {
|
|
396
423
|
app.log.warn(`[runs] delivery resolution for ${runId} failed (field absent until restart replays the trail): ${err instanceof Error ? err.message : String(err)}`);
|
|
397
424
|
}
|
|
398
425
|
};
|
|
426
|
+
// Crew reaches a bus only through the engine that holds it (wicked-core#631, core/bus.ts): a seam
|
|
427
|
+
// with no bus db of its own reads and writes the one this adapter handed its engine.
|
|
428
|
+
const engineBusDb = typeof adapter.busDbPath === 'string' ? adapter.busDbPath : undefined;
|
|
429
|
+
const busOf = (dbPath) => {
|
|
430
|
+
const bus = dbPath ?? engineBusDb;
|
|
431
|
+
return bus !== undefined ? { dbPath: bus } : {};
|
|
432
|
+
};
|
|
399
433
|
const projectBus = options?.projectEvents?.disabled === true
|
|
400
434
|
? null
|
|
401
435
|
: await startProjectBus({
|
|
402
|
-
...(options?.projectEvents?.dbPath
|
|
403
|
-
? { dbPath: options.projectEvents.dbPath }
|
|
404
|
-
: {}),
|
|
436
|
+
...busOf(options?.projectEvents?.dbPath),
|
|
405
437
|
...(options?.projectEvents?.pollIntervalMs !== undefined
|
|
406
438
|
? { pollIntervalMs: options.projectEvents.pollIntervalMs }
|
|
407
439
|
: {}),
|
|
@@ -422,7 +454,6 @@ export async function createServer(adapter, options) {
|
|
|
422
454
|
// is still preferred over its file once armed; the seams themselves arm further down.
|
|
423
455
|
let draftSub = null;
|
|
424
456
|
let editSub = null;
|
|
425
|
-
let demoSub = null;
|
|
426
457
|
let chatSub = null;
|
|
427
458
|
/** The crew-side half of deleting an interactive doc (crew#338): drop the doc's replay-dedup
|
|
428
459
|
* rows from all four handoff ledgers — the draft leg keys by DOCUMENT ID ("one first draft
|
|
@@ -433,11 +464,11 @@ export async function createServer(adapter, options) {
|
|
|
433
464
|
* override, crew#353/#398). Never throws — the report says what happened. */
|
|
434
465
|
const crewStateDir = crewStateHome();
|
|
435
466
|
// F-046: the create-time doc → subject-repo bindings, shared by the proxy (records) and the
|
|
436
|
-
// draft
|
|
467
|
+
// draft seam (read). The store keeps NO file of its own — each binding is a
|
|
437
468
|
// `crew-grounding.json` sidecar beside the doc's `versions.json` under the project's docs root
|
|
438
469
|
// (doc-grounding.ts says why not the state home: core's fence refuses unregistered entries).
|
|
439
470
|
const docGrounding = new DocGroundingStore();
|
|
440
|
-
/** The
|
|
471
|
+
/** The three seams' ledgers, read AT USE TIME (a seam that armed after this closure was built is
|
|
441
472
|
* still preferred over its file) — shared by the doc-delete sweep and the doc↔run index. */
|
|
442
473
|
const docLedgerSources = () => [
|
|
443
474
|
{
|
|
@@ -458,12 +489,6 @@ export async function createServer(adapter, options) {
|
|
|
458
489
|
path: options?.interactiveChatEvents?.ledgerPath ??
|
|
459
490
|
join(crewStateDir, 'interactive-chat-ledger.json'),
|
|
460
491
|
},
|
|
461
|
-
{
|
|
462
|
-
name: 'demo',
|
|
463
|
-
ledger: demoSub?.ledger,
|
|
464
|
-
path: options?.interactiveDemoEvents?.ledgerPath ??
|
|
465
|
-
join(crewStateDir, 'interactive-demo-ledger.json'),
|
|
466
|
-
},
|
|
467
492
|
];
|
|
468
493
|
const dropDocLedgerRows = (documentId) => sweepDocLedgers(documentId, docLedgerSources());
|
|
469
494
|
// Wave 6 (F-4R2-006 root fix): the document ↔ run binding as a direct read off the SAME four
|
|
@@ -477,9 +502,7 @@ export async function createServer(adapter, options) {
|
|
|
477
502
|
const interactiveRelay = options?.interactiveWsRelay?.disabled === true
|
|
478
503
|
? null
|
|
479
504
|
: await startInteractiveWsRelay({
|
|
480
|
-
...(options?.interactiveWsRelay?.dbPath
|
|
481
|
-
? { dbPath: options.interactiveWsRelay.dbPath }
|
|
482
|
-
: {}),
|
|
505
|
+
...busOf(options?.interactiveWsRelay?.dbPath),
|
|
483
506
|
...(options?.interactiveWsRelay?.pollIntervalMs !== undefined
|
|
484
507
|
? { pollIntervalMs: options.interactiveWsRelay.pollIntervalMs }
|
|
485
508
|
: {}),
|
|
@@ -506,6 +529,31 @@ export async function createServer(adapter, options) {
|
|
|
506
529
|
await interactiveRelay.stop();
|
|
507
530
|
});
|
|
508
531
|
}
|
|
532
|
+
// The team relay (DES-TEAMING-002 §4.5): every team row the engine publishes becomes a
|
|
533
|
+
// `teamEvent` frame on the same /ws socket, tagged with the run's project. Only where the engine
|
|
534
|
+
// has a bus: no bus, no team rows.
|
|
535
|
+
const teamBusDb = engineBusDb;
|
|
536
|
+
const teamRelay = options?.teamWsRelay?.disabled === true || teamBusDb === undefined
|
|
537
|
+
? null
|
|
538
|
+
: await startTeamWsRelay({
|
|
539
|
+
dbPath: teamBusDb,
|
|
540
|
+
projectOf: (runId) => membershipIndex.projectOf(runId),
|
|
541
|
+
// Standing orders (behaviour 10) read the same team rows: a finding can wake the operator.
|
|
542
|
+
broadcast: (frame) => {
|
|
543
|
+
broadcast(frame);
|
|
544
|
+
void standingOrderEvaluator?.onTeamFrame(frame);
|
|
545
|
+
},
|
|
546
|
+
...(options?.teamWsRelay?.pollIntervalMs !== undefined
|
|
547
|
+
? { pollIntervalMs: options.teamWsRelay.pollIntervalMs }
|
|
548
|
+
: {}),
|
|
549
|
+
log: (m) => app.log.warn(m),
|
|
550
|
+
});
|
|
551
|
+
if (teamRelay !== null) {
|
|
552
|
+
app.log.info('team /ws relay armed (team rows → teamEvent)');
|
|
553
|
+
app.addHook('onClose', async () => {
|
|
554
|
+
await teamRelay.stop();
|
|
555
|
+
});
|
|
556
|
+
}
|
|
509
557
|
/** The post-commit half of a project-FILED launch, shared with the launch route (§2.2/§4):
|
|
510
558
|
* the engine already attached the crew.run membership atomically with the launch — here we
|
|
511
559
|
* tag future /ws frames and announce the attach on the project bus. */
|
|
@@ -520,34 +568,16 @@ export async function createServer(adapter, options) {
|
|
|
520
568
|
/** The docs root a project's interactive docs live under — the SAME per-project resolution
|
|
521
569
|
* the project routes and the interactive proxy use (DES-MERGE-001 §7.1/§7.2; partitioned
|
|
522
570
|
* per project since crew#472, with an event that carries no `project_id` belonging to
|
|
523
|
-
* Unfiled). Shared by the edit seam (demo-kind gate
|
|
524
|
-
* install + manifest reads), and the chat seam. The partition is containment-checked on
|
|
571
|
+
* Unfiled). Shared by the edit seam (demo-kind gate) and the chat seam. The partition is containment-checked on
|
|
525
572
|
* REAL paths here exactly as the routes check it (crew#474 — one walk, `bridge-root.ts`):
|
|
526
573
|
* a symlinked `projects/<id>` throws `InteractivePartitionRefusedError` into the seam's
|
|
527
574
|
* handler (logged by its `onError`, the event unanswered — fail closed) instead of being
|
|
528
575
|
* followed into another project's docs; the seams only READ under a root the routes
|
|
529
576
|
* materialized, so a missing partition is returned as spelled and nothing is created. */
|
|
530
577
|
const interactiveDocsRoot = (projectId) => resolveProjectInteractiveRoot(projectId, projectId !== undefined ? projectSettings.get(projectId) : null);
|
|
531
|
-
// Arm the opt-in QE gate-event subscription (crew's bus seam). Failure to
|
|
532
|
-
// arm is LOUD but non-fatal: the acceptance route never depends on the bus.
|
|
533
|
-
if (options?.qeGateEvents?.enabled === true) {
|
|
534
|
-
const { dbPath, pollIntervalMs } = options.qeGateEvents;
|
|
535
|
-
const sub = await startQeGateSubscriber(qeGateCache, {
|
|
536
|
-
...(dbPath !== undefined ? { dbPath } : {}),
|
|
537
|
-
...(pollIntervalMs !== undefined ? { pollIntervalMs } : {}),
|
|
538
|
-
log: (m) => app.log.warn(m),
|
|
539
|
-
logError: (m) => app.log.error(m),
|
|
540
|
-
});
|
|
541
|
-
if (sub !== null) {
|
|
542
|
-
app.log.info(`qe gate-event subscription armed (filter wicked.qe.**)`);
|
|
543
|
-
app.addHook('onClose', async () => {
|
|
544
|
-
await sub.stop();
|
|
545
|
-
});
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
578
|
// ── A STUB ENGINE NEVER ANSWERS ANOTHER PRODUCT'S TRAFFIC (crew#309) ────────────────────────
|
|
549
579
|
//
|
|
550
|
-
// The four seams below are ANSWERERS: each
|
|
580
|
+
// The four seams below are ANSWERERS: each taps the bus (core/bus.ts) and replies
|
|
551
581
|
// to wicked-interactive's events by LAUNCHING A GOVERNED RUN. Under `serve --stub` the engine is
|
|
552
582
|
// `Core.spawnStub` — a `StubDispatcher` (every seat votes for the first roster option, no
|
|
553
583
|
// subprocess) plus a `StubStepRunner` (fixed text, no CLI) — so such a run resolves every phase
|
|
@@ -556,7 +586,8 @@ export async function createServer(adapter, options) {
|
|
|
556
586
|
// approved outline — moving on…", "Gate approved the draft…". Nothing ran. Nothing was written.
|
|
557
587
|
// Two gate approvals were announced anyway.
|
|
558
588
|
//
|
|
559
|
-
// Worse than fabricating locally
|
|
589
|
+
// Worse than fabricating locally (until crew#679 moved the seams to in-memory taps): the durable
|
|
590
|
+
// cursor was keyed by PLUGIN NAME
|
|
560
591
|
// (`wicked-crew-interactive-draft`), so on the shared bus a stub daemon and the production
|
|
561
592
|
// daemon are ONE consumer group — a frame claimed by the stub is a frame the real daemon never
|
|
562
593
|
// sees. That is what wicked-crew#309 recorded: bus rows 242632–242643 carry a complete draft
|
|
@@ -567,8 +598,8 @@ export async function createServer(adapter, options) {
|
|
|
567
598
|
// convening, which is the StubDispatcher's signature; the real runs 5 minutes later took ~95s to
|
|
568
599
|
// vote and split their seats (`pi` for outline, `claude` for draft).
|
|
569
600
|
//
|
|
570
|
-
// So: refuse to arm, loudly. The deny is scoped to the ANSWERERS on purpose — the
|
|
571
|
-
// only
|
|
601
|
+
// So: refuse to arm, loudly. The deny is scoped to the ANSWERERS on purpose — the project seam
|
|
602
|
+
// only relays, neither launches work nor
|
|
572
603
|
// narrates governance, so neither can fabricate a verdict. Offline/deterministic runs are NOT
|
|
573
604
|
// lost by this: every harness in `e2e/` already does the correct thing, keeping the REAL engine
|
|
574
605
|
// (`stub: false`) and registering a scripted stub SEAT in the roster — which exercises planning,
|
|
@@ -593,7 +624,7 @@ export async function createServer(adapter, options) {
|
|
|
593
624
|
if (options?.interactiveDraftEvents?.enabled === true && !refuseStubSeam('interactive-draft')) {
|
|
594
625
|
const o = options.interactiveDraftEvents;
|
|
595
626
|
draftSub = await startInteractiveDraftSubscriber(adapter, {
|
|
596
|
-
...(o.dbPath
|
|
627
|
+
...busOf(o.dbPath),
|
|
597
628
|
...(o.pollIntervalMs !== undefined ? { pollIntervalMs: o.pollIntervalMs } : {}),
|
|
598
629
|
...(o.heartbeatMs !== undefined ? { heartbeatMs: o.heartbeatMs } : {}),
|
|
599
630
|
...(o.ledgerPath !== undefined ? { ledgerPath: o.ledgerPath } : {}),
|
|
@@ -627,14 +658,10 @@ export async function createServer(adapter, options) {
|
|
|
627
658
|
// Arm the opt-in interactive STRUCTURAL-edit answering seam (task #86 final leg). Same
|
|
628
659
|
// posture as the draft seam: failure to arm is LOUD but non-fatal — interactive's assist
|
|
629
660
|
// loop is the fallback answerer, and this daemon must boot on a machine whose bus is broken.
|
|
630
|
-
// `demoSub` (declared above) is deliberately still null here: the demo seam arms after (it
|
|
631
|
-
// must, so the per-doc serialization contract can see the edit seam's in-flight set), but the
|
|
632
|
-
// edit seam's demo-kind gate needs to know at EVENT time whether the demo seam actually came
|
|
633
|
-
// up — a closure over the binding reads the settled value; a boolean captured here would not.
|
|
634
661
|
if (options?.interactiveEditEvents?.enabled === true && !refuseStubSeam('interactive-edit')) {
|
|
635
662
|
const o = options.interactiveEditEvents;
|
|
636
663
|
editSub = await startInteractiveEditSubscriber(adapter, {
|
|
637
|
-
...(o.dbPath
|
|
664
|
+
...busOf(o.dbPath),
|
|
638
665
|
...(o.pollIntervalMs !== undefined ? { pollIntervalMs: o.pollIntervalMs } : {}),
|
|
639
666
|
...(o.heartbeatMs !== undefined ? { heartbeatMs: o.heartbeatMs } : {}),
|
|
640
667
|
...(o.ledgerPath !== undefined ? { ledgerPath: o.ledgerPath } : {}),
|
|
@@ -645,11 +672,9 @@ export async function createServer(adapter, options) {
|
|
|
645
672
|
// The quality-floor skill gate (draft-skill.ts): stamped only when the published snapshot holds it;
|
|
646
673
|
// the arm-time answer is recorded for /diagnostics (crew#661).
|
|
647
674
|
skillHeld: phaseSkills.probe('interactive-edit', INTERACTIVE_EDIT_WORKFLOW_DEF, skillHeld),
|
|
648
|
-
// The demo-kind gate
|
|
649
|
-
//
|
|
650
|
-
// un-armed demo seam gets an honest error status instead of a silent, unanswerable drop.
|
|
675
|
+
// The demo-kind gate: a demo doc's step feedback is declined with an honest error status
|
|
676
|
+
// (demos are made by the Demo experience's `demo` preset now, studio#373).
|
|
651
677
|
resolveDocsRoot: o.resolveDocsRoot ?? interactiveDocsRoot,
|
|
652
|
-
demoSeamArmed: () => demoSub !== null,
|
|
653
678
|
onRunFiled: fileRun,
|
|
654
679
|
log: (m) => app.log.warn(m),
|
|
655
680
|
logError: (m) => app.log.error(m),
|
|
@@ -665,37 +690,6 @@ export async function createServer(adapter, options) {
|
|
|
665
690
|
});
|
|
666
691
|
}
|
|
667
692
|
}
|
|
668
|
-
// Arm the opt-in interactive DEMO answering seam (CREW-UX-9 — the retired assist agent's
|
|
669
|
-
// Step 8). Same posture as the sibling seams: failure to arm is LOUD but non-fatal — a demo
|
|
670
|
-
// doc then keeps its placeholder, and this daemon must boot on a machine whose bus is
|
|
671
|
-
// broken. Armed beside the edit seam because the two split `feedback.processed` by the doc
|
|
672
|
-
// manifest's kind (demo → re-author the spec here; everything else → structural edit there).
|
|
673
|
-
if (options?.interactiveDemoEvents?.enabled === true && !refuseStubSeam('interactive-demo')) {
|
|
674
|
-
const o = options.interactiveDemoEvents;
|
|
675
|
-
demoSub = await startInteractiveDemoSubscriber(adapter, {
|
|
676
|
-
...(o.dbPath !== undefined ? { dbPath: o.dbPath } : {}),
|
|
677
|
-
...(o.pollIntervalMs !== undefined ? { pollIntervalMs: o.pollIntervalMs } : {}),
|
|
678
|
-
...(o.heartbeatMs !== undefined ? { heartbeatMs: o.heartbeatMs } : {}),
|
|
679
|
-
...(o.ledgerPath !== undefined ? { ledgerPath: o.ledgerPath } : {}),
|
|
680
|
-
...(o.demoDir !== undefined ? { demoDir: o.demoDir } : {}),
|
|
681
|
-
...(o.clisJson !== undefined ? { clisJson: o.clisJson } : {}),
|
|
682
|
-
// The roster WITH standing when no override is set (F-RECON-002/003).
|
|
683
|
-
roster: rosterWithStanding,
|
|
684
|
-
resolveDocsRoot: o.resolveDocsRoot ?? interactiveDocsRoot,
|
|
685
|
-
onRunFiled: fileRun,
|
|
686
|
-
onRunLaunched: (runId, detail) => { recordRunLaunched(audit, runTimingIndex, DAEMON_ACTOR, runId, detail); },
|
|
687
|
-
groundingStore: docGrounding,
|
|
688
|
-
log: (m) => app.log.warn(m),
|
|
689
|
-
logError: (m) => app.log.error(m),
|
|
690
|
-
});
|
|
691
|
-
if (demoSub !== null) {
|
|
692
|
-
const sub = demoSub;
|
|
693
|
-
app.log.info('interactive-demo subscription armed (filters wicked.interactive.doc.created + feedback.processed, kind:demo)');
|
|
694
|
-
app.addHook('onClose', async () => {
|
|
695
|
-
await sub.stop();
|
|
696
|
-
});
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
693
|
// Arm the opt-in interactive CHAT answering seam (CREW-UX-5 — the iteration ask). Same
|
|
700
694
|
// posture again: failure to arm is LOUD but non-fatal. Armed AFTER the sibling seams so the
|
|
701
695
|
// per-doc serialization contract (no draft/edit/chat run races another on one doc) can
|
|
@@ -704,7 +698,7 @@ export async function createServer(adapter, options) {
|
|
|
704
698
|
if (options?.interactiveChatEvents?.enabled === true && !refuseStubSeam('interactive-chat')) {
|
|
705
699
|
const o = options.interactiveChatEvents;
|
|
706
700
|
chatSub = await startInteractiveChatSubscriber(adapter, {
|
|
707
|
-
...(o.dbPath
|
|
701
|
+
...busOf(o.dbPath),
|
|
708
702
|
...(o.pollIntervalMs !== undefined ? { pollIntervalMs: o.pollIntervalMs } : {}),
|
|
709
703
|
...(o.heartbeatMs !== undefined ? { heartbeatMs: o.heartbeatMs } : {}),
|
|
710
704
|
...(o.ledgerPath !== undefined ? { ledgerPath: o.ledgerPath } : {}),
|
|
@@ -753,14 +747,23 @@ export async function createServer(adapter, options) {
|
|
|
753
747
|
// candidates. Views with no units (older engines, stub adapters) keep the historical
|
|
754
748
|
// `unit_ix` fallback.
|
|
755
749
|
const cursor = resolveCursorUnit(v);
|
|
756
|
-
//
|
|
757
|
-
//
|
|
758
|
-
//
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
750
|
+
// Evaluator ≠ creator across a failover: an EVALUATOR cursor avoids the seats that
|
|
751
|
+
// built the work it reviews (DES-L3 PR-3E), a CREATOR cursor avoids the run's
|
|
752
|
+
// evaluator seats (crew#638), and a unit the engine routed `evaluator_distinct`
|
|
753
|
+
// avoids the seat it was routed away from (crew#583). A free-text unit carries no role
|
|
754
|
+
// and no such routing → no constraint.
|
|
755
|
+
const seatsOfRole = (role) => v.units
|
|
756
|
+
.filter((u) => u.role === role && u.assigned_cli != null)
|
|
757
|
+
.map((u) => u.assigned_cli);
|
|
758
|
+
const cursorUnit = cursor === undefined ? undefined : v.units.find((u) => u.ord === cursor.ord);
|
|
759
|
+
const routing = cursorUnit?.routing;
|
|
760
|
+
const avoid = [
|
|
761
|
+
...new Set([
|
|
762
|
+
...(cursor?.role === 'evaluator' ? seatsOfRole('creator') : []),
|
|
763
|
+
...(cursor?.role === 'creator' ? seatsOfRole('evaluator') : []),
|
|
764
|
+
...(routing?.method === 'evaluator_distinct' ? [routing.was] : []),
|
|
765
|
+
]),
|
|
766
|
+
].filter((s) => s !== cursor?.cli);
|
|
764
767
|
return {
|
|
765
768
|
id: v.session.id,
|
|
766
769
|
ord: cursor?.ord ?? v.session.unit_ix,
|
|
@@ -820,6 +823,13 @@ export async function createServer(adapter, options) {
|
|
|
820
823
|
onTurnTimeout: ({ session, ...detail }) => {
|
|
821
824
|
audit.record('run.turn.timedout', { id: 'stall-watchdog', kind: 'system', trust: 'admin' }, { runId: session, detail });
|
|
822
825
|
},
|
|
826
|
+
// crew#629: a unit whose own processes are working (a test runner, a build) is not silent.
|
|
827
|
+
...(options?.stallWatchdog?.busy === null
|
|
828
|
+
? {}
|
|
829
|
+
: {
|
|
830
|
+
busy: options?.stallWatchdog?.busy ??
|
|
831
|
+
((sampler) => (runIds) => sampler.busy(runIds))(new RunLivenessSampler()),
|
|
832
|
+
}),
|
|
823
833
|
log: (m) => app.log.warn(m),
|
|
824
834
|
});
|
|
825
835
|
// The daemon's single CoreEvent subscription fans out here: cache gate prompts
|
|
@@ -846,6 +856,51 @@ export async function createServer(adapter, options) {
|
|
|
846
856
|
// written from the stamped frames below, dropped with the chat on `chatClosed`, served on
|
|
847
857
|
// `GET /chats/:id.messages`.
|
|
848
858
|
const chatTranscripts = new ChatTranscriptStore();
|
|
859
|
+
// Chat citations (crew#561, F-RC1-117): what the seat CITED, checked against the very read roots
|
|
860
|
+
// the daemon handed it. Runs off the reply's own path — after the broadcast below — and publishes
|
|
861
|
+
// one `chatCitations` frame per reply; the seat's text is never edited.
|
|
862
|
+
const citationsDisabled = options?.chatCitations?.disabled === true;
|
|
863
|
+
const citationDeps = options?.chatCitations?.deps ?? chatCitationDeps();
|
|
864
|
+
const citationLimits = options?.chatCitations?.limits;
|
|
865
|
+
async function verifyReplyCitations(frame, projectId) {
|
|
866
|
+
const f = frame;
|
|
867
|
+
const chatId = typeof f['chat'] === 'string' ? f['chat'] : undefined;
|
|
868
|
+
const cliKey = typeof f['cliKey'] === 'string' ? f['cliKey'] : undefined;
|
|
869
|
+
const text = typeof f['text'] === 'string' ? f['text'] : '';
|
|
870
|
+
// A NOT-ok reply is the daemon's/engine's own reason line (an eviction, a refusal): it cites
|
|
871
|
+
// nothing the reader would paste, so it is not verified — and never marked.
|
|
872
|
+
if (chatId === undefined || cliKey === undefined || text === '' || f['ok'] === false)
|
|
873
|
+
return;
|
|
874
|
+
const scope = chatScopes.get(chatId);
|
|
875
|
+
// No scope (a chat this daemon did not open, or `kind: 'none'`/`system`) ⇒ nothing to verify
|
|
876
|
+
// AGAINST. Saying "unverified" then would blame the seat for the daemon's missing roots.
|
|
877
|
+
if (scope === undefined || scope.repos.length === 0)
|
|
878
|
+
return;
|
|
879
|
+
const roots = scope.repos.map((r) => ({ absRoot: resolvePath(r.rootPath), name: r.name }));
|
|
880
|
+
const turnId = typeof f['turn_id'] === 'string' ? f['turn_id'] : undefined;
|
|
881
|
+
// A chat is filed by its CHAT id (`crew.chat` membership), so the project of a chat frame is
|
|
882
|
+
// looked up by that — the run-keyed lookup on the relay above answers `undefined` for chats.
|
|
883
|
+
const project = projectId ?? membershipIndex.projectOf(chatId);
|
|
884
|
+
try {
|
|
885
|
+
const result = await verifyCitations(text, roots, citationDeps, citationLimits);
|
|
886
|
+
const out = citationsFrame({
|
|
887
|
+
chat: chatId,
|
|
888
|
+
cliKey,
|
|
889
|
+
...(turnId !== undefined ? { turn_id: turnId } : {}),
|
|
890
|
+
...(project !== undefined ? { project_id: project } : {}),
|
|
891
|
+
}, result);
|
|
892
|
+
if (out !== null) {
|
|
893
|
+
broadcast(out);
|
|
894
|
+
// ...and into the transcript, so a reload still shows the marks (api-types 0.68.0): the
|
|
895
|
+
// verdicts are their own append-only record, folded onto their reply by the reader.
|
|
896
|
+
chatTranscripts.recordCitations(out);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
catch (err) {
|
|
900
|
+
// Loud-non-fatal: a reply is never held hostage to its own verification.
|
|
901
|
+
app.log.warn(`[chat] citation verification failed for ${chatId}/${cliKey}: ${err instanceof Error ? err.message : String(err)}`);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
849
904
|
// crew#619: see declaration of chatRetained/runToChat above (before the hydration block).
|
|
850
905
|
// linkChatRun populates both maps and is called from the hydration block (function-hoisted)
|
|
851
906
|
// and from the launch route (via RoutesRuntime.linkChatRun).
|
|
@@ -908,6 +963,13 @@ export async function createServer(adapter, options) {
|
|
|
908
963
|
const session = typeof event.session === 'string' ? event.session : undefined;
|
|
909
964
|
const projectId = session !== undefined ? membershipIndex.projectOf(session) : undefined;
|
|
910
965
|
broadcast(projectId !== undefined ? { ...rewritten, project_id: projectId } : rewritten);
|
|
966
|
+
// crew#561: the citations in that reply, checked against the chat's read roots — AFTER the
|
|
967
|
+
// broadcast (the answer is never delayed by verification) and never awaited on this path.
|
|
968
|
+
if (!citationsDisabled && rewritten.type === 'chatReply')
|
|
969
|
+
void verifyReplyCitations(rewritten, projectId);
|
|
970
|
+
// Standing orders (behaviour 10): a gate that opened may be one an order answers, holds or
|
|
971
|
+
// reports — after the gate cache folded it, so the order's decision carries the right ord.
|
|
972
|
+
void standingOrderEvaluator?.onEvent(event);
|
|
911
973
|
// The delivered-PR record (CREW-UX-8, crew#321): resolved once per run at its terminal
|
|
912
974
|
// frame, best-effort, off the hot path — see `resolveRunDelivery` above for why BOTH
|
|
913
975
|
// terminal frames trigger it and why a failed deliver is a no-op. THEN the delivery-
|
|
@@ -977,7 +1039,6 @@ export async function createServer(adapter, options) {
|
|
|
977
1039
|
adapter,
|
|
978
1040
|
audit,
|
|
979
1041
|
index: testSets,
|
|
980
|
-
workflows: () => adapter.listWorkflows(),
|
|
981
1042
|
deliveryUrlFor: (runId) => deliveryIndex.urlFor(runId),
|
|
982
1043
|
labelFor: (runId) => {
|
|
983
1044
|
const attach = groupIndex.attachOf(runId);
|
|
@@ -1080,14 +1141,17 @@ export async function createServer(adapter, options) {
|
|
|
1080
1141
|
try {
|
|
1081
1142
|
done(null, JSON.parse(body));
|
|
1082
1143
|
}
|
|
1083
|
-
catch
|
|
1144
|
+
catch {
|
|
1084
1145
|
// A syntactically invalid body is the CLIENT's error: without an explicit statusCode
|
|
1085
1146
|
// Fastify reports a parser error as a 500, which misfiles "you sent `{not json`" as a
|
|
1086
1147
|
// server fault — on the elicitation answer path that told an operator the daemon broke
|
|
1087
1148
|
// when their request did. 400 matches Fastify's own default JSON parser
|
|
1088
1149
|
// (FST_ERR_CTP_INVALID_JSON) and every schema-level 400 these routes already return.
|
|
1089
|
-
|
|
1090
|
-
|
|
1150
|
+
// The message is a fixed one, never JSON.parse's: V8 quotes the body around the bad token
|
|
1151
|
+
// (`Unexpected token 's', ..."{"value": sk-…"... is not valid JSON`), and Fastify both
|
|
1152
|
+
// answers and logs it, so an unquoted `PUT /mcp/servers/:name/secret` value would leave the
|
|
1153
|
+
// broker in the response and the log (DES-MCP-TOOLS-001 D-2).
|
|
1154
|
+
done(Object.assign(new Error('the request body is not valid JSON'), { statusCode: 400, code: 'FST_ERR_CTP_INVALID_JSON' }));
|
|
1091
1155
|
}
|
|
1092
1156
|
});
|
|
1093
1157
|
await app.register(fastifyWebsocket);
|
|
@@ -1109,13 +1173,47 @@ export async function createServer(adapter, options) {
|
|
|
1109
1173
|
// diagnostics reads the bundle's shipped version manifest from the same root the static
|
|
1110
1174
|
// handler serves. One resolution, two consumers.
|
|
1111
1175
|
const studioRoot = options?.studioRoot ?? defaultStudioRoot();
|
|
1112
|
-
|
|
1176
|
+
// DES-MCP-TOOLS-001 S2: the registry over `<state home>/mcp` (created on the first save).
|
|
1177
|
+
const mcpRegistry = new McpRegistry({
|
|
1178
|
+
store: new McpRegistryStore(),
|
|
1179
|
+
secrets: platformSecretStore(),
|
|
1180
|
+
probe: probeMcpServer,
|
|
1181
|
+
// Read the worker home at request time: `PUT /settings` can move it (applyWorkerConfigRoot).
|
|
1182
|
+
discover: (managed) => discoverMcpServers({ home: homedir(), workerHome: process.env['WICKED_WORKER_HOME'] ?? null }, managed),
|
|
1183
|
+
});
|
|
1184
|
+
// S3: every brokered call is judged by the in-process engine (the token registry is the
|
|
1185
|
+
// engine's, process-global), recorded to `<state home>/mcp/calls.ndjson`, and published on /ws
|
|
1186
|
+
// and the engine's bus. A bus that is not attached only loses the event; the record is the record.
|
|
1187
|
+
// S7: the same file (one serialized chain) is what `GET /mcp/usage` reads back and folds.
|
|
1188
|
+
const mcpCallRecords = new McpCallRecordFile();
|
|
1189
|
+
const mcpBroker = new McpBroker({
|
|
1190
|
+
registry: mcpRegistry,
|
|
1191
|
+
engine: () => CoreAdapter.mcpEngineGate(),
|
|
1192
|
+
records: mcpCallRecords,
|
|
1193
|
+
budgetPerUnit: budgetFromEnv(),
|
|
1194
|
+
log: (m) => app.log.warn(m),
|
|
1195
|
+
publish: async (record) => {
|
|
1196
|
+
broadcast({ type: 'mcpCallCompleted', record });
|
|
1197
|
+
if (engineBusDb === undefined)
|
|
1198
|
+
return;
|
|
1199
|
+
await emitOnBus(engineBusDb, {
|
|
1200
|
+
event_type: MCP_CALL_COMPLETED,
|
|
1201
|
+
domain: 'wicked-crew',
|
|
1202
|
+
subdomain: 'mcp',
|
|
1203
|
+
payload: record,
|
|
1204
|
+
producer_id: 'wicked-crew',
|
|
1205
|
+
idempotency_key: `${MCP_CALL_COMPLETED}:${record.spanId}`,
|
|
1206
|
+
});
|
|
1207
|
+
},
|
|
1208
|
+
});
|
|
1209
|
+
const registered = registerRoutes(app, adapter, gateCache, elicitationCache, {
|
|
1113
1210
|
bus: projectBus,
|
|
1114
1211
|
index: membershipIndex,
|
|
1115
1212
|
log: (m) => app.log.warn(m),
|
|
1116
1213
|
settings: projectSettings,
|
|
1117
1214
|
}, { audit, authMode: auth.mode }, {
|
|
1118
1215
|
seatHealth,
|
|
1216
|
+
deliveryFreeze,
|
|
1119
1217
|
// wicked-studio#284: the watchdog's remembered frames ride `GET /runs/:id/events`.
|
|
1120
1218
|
stallFrames: (runId) => stallFrameIndex.framesFor(runId),
|
|
1121
1219
|
// The SAME standing accessor the seams and the adapter launch with (F-RECON-002/003).
|
|
@@ -1149,6 +1247,13 @@ export async function createServer(adapter, options) {
|
|
|
1149
1247
|
interactiveBridgeBusDataDir: options?.interactiveBridge?.busDataDir ?? null,
|
|
1150
1248
|
docGrounding,
|
|
1151
1249
|
...(skillsRuntime !== undefined ? { skills: skillsRuntime } : {}),
|
|
1250
|
+
// DES-MCP-TOOLS-001 S2: the registry over `<state home>/mcp` (created on the first save).
|
|
1251
|
+
// Route tests drive `registerMcpRoutes` over their own registry (tests/mcp-registry.test.ts).
|
|
1252
|
+
mcp: mcpRegistry,
|
|
1253
|
+
// DES-MCP-TOOLS-001 S3: the broker's call path over that registry.
|
|
1254
|
+
mcpBroker,
|
|
1255
|
+
// DES-MCP-TOOLS-001 S7: the call records the usage fold reads.
|
|
1256
|
+
mcpCalls: mcpCallRecords,
|
|
1152
1257
|
// wicked-core#411 / crew#497: the live state-home classification the routes report and gate on.
|
|
1153
1258
|
stateHome: stateHomeWatch,
|
|
1154
1259
|
// Routes that say something to the thread (a refused chat seat, F-2R2-007) emit through the
|
|
@@ -1157,6 +1262,68 @@ export async function createServer(adapter, options) {
|
|
|
1157
1262
|
// crew#619: retain a chat's transcript for the lifetime of its promoted run.
|
|
1158
1263
|
linkChatRun,
|
|
1159
1264
|
});
|
|
1265
|
+
// Standing orders (Studio OS behaviour 10): the store, the evaluator over THE gate decision path
|
|
1266
|
+
// (`registered.decideGate`), and the routes. See src/standing-orders/.
|
|
1267
|
+
// The durable record is the audit trail (store.ts): fold it once, here.
|
|
1268
|
+
const standingOrders = new StandingOrderStore(audit);
|
|
1269
|
+
await standingOrders.hydrate(audit, (m) => app.log.warn(m));
|
|
1270
|
+
standingOrderEvaluator = new StandingOrderEvaluator({
|
|
1271
|
+
store: standingOrders,
|
|
1272
|
+
decideGate: registered.decideGate,
|
|
1273
|
+
audit,
|
|
1274
|
+
runFacts: async (runId) => {
|
|
1275
|
+
const v = (await adapter.sessionsDetail()).find((x) => x.session.id === runId);
|
|
1276
|
+
if (v === undefined)
|
|
1277
|
+
return undefined;
|
|
1278
|
+
return {
|
|
1279
|
+
projectId: runProject(v, runId, (id) => membershipIndex.projectOf(id)),
|
|
1280
|
+
band: runBand(v),
|
|
1281
|
+
preset: runPreset(v),
|
|
1282
|
+
problem: v.session.problem,
|
|
1283
|
+
phaseOf: (ord) => v.units.find((u) => u.ord === ord)?.phase_ref ?? undefined,
|
|
1284
|
+
firstOrd: v.units.length === 0 ? undefined : Math.min(...v.units.map((u) => u.ord)),
|
|
1285
|
+
landsDoctrine: isSteeringAuthorRun(v),
|
|
1286
|
+
};
|
|
1287
|
+
},
|
|
1288
|
+
openGates: async () => {
|
|
1289
|
+
if (typeof adapter.interactionRequests !== 'function')
|
|
1290
|
+
return [];
|
|
1291
|
+
const rows = (await adapter.interactionRequests(undefined, 'open')) ?? [];
|
|
1292
|
+
return rows
|
|
1293
|
+
.filter((r) => r.kind === 'gate' && typeof r.ord === 'number')
|
|
1294
|
+
.map((r) => ({
|
|
1295
|
+
runId: r.session_id,
|
|
1296
|
+
ord: r.ord,
|
|
1297
|
+
reviewingOrd: r.reviewing_ord,
|
|
1298
|
+
gateKind: typeof r['gate_kind'] === 'string' ? r['gate_kind'] : '',
|
|
1299
|
+
prompt: r.prompt,
|
|
1300
|
+
}));
|
|
1301
|
+
},
|
|
1302
|
+
// Brainstorm idea 13: a plan-approval trust order re-reads the open plan gate's own band.
|
|
1303
|
+
planGate: async (runId) => openPlanGateRisk(await busRows(adapter.busDbPath, runId)),
|
|
1304
|
+
log: (m) => app.log.warn(m),
|
|
1305
|
+
});
|
|
1306
|
+
// The decided-gate history (brainstorm ideas 7 and 8): a seat's track record, and the preview
|
|
1307
|
+
// of what a proposed order would have done — read with the evaluator's own phase and invariant.
|
|
1308
|
+
registerGateHistoryRoute(app, {
|
|
1309
|
+
audit,
|
|
1310
|
+
views: () => adapter.sessionsDetail(),
|
|
1311
|
+
gateRows: async () => typeof adapter.interactionRequests === 'function' ? ((await adapter.interactionRequests(undefined, undefined)) ?? []) : [],
|
|
1312
|
+
projectOf: (id) => membershipIndex.projectOf(id),
|
|
1313
|
+
landsDoctrine: isSteeringAuthorRun,
|
|
1314
|
+
});
|
|
1315
|
+
registerStandingOrderRoutes(app, {
|
|
1316
|
+
store: standingOrders,
|
|
1317
|
+
evaluator: standingOrderEvaluator,
|
|
1318
|
+
parse: seatParser({ adapter, roster: rosterWithStanding }),
|
|
1319
|
+
});
|
|
1320
|
+
// Orders hydrated from the trail act on gates ALREADY open: one sweep of the engine's open gate
|
|
1321
|
+
// rows at boot. It also covers any gate that opened before the evaluator existed (the live
|
|
1322
|
+
// subscription above reads it late-bound); every later gate arrives as a live frame.
|
|
1323
|
+
void standingOrderEvaluator.sweep().catch((err) => app.log.warn(`[standing-orders] boot sweep failed: ${String(err)}`));
|
|
1324
|
+
// crew#471: a campaign the previous daemon left `running` is crash-resumed by the engine (its
|
|
1325
|
+
// node runs froze with that process) — asked once, at boot, never for a paused one.
|
|
1326
|
+
void resumeRunningCampaigns(adapter, (m) => app.log.warn(m));
|
|
1160
1327
|
// The UI-emittable direction of the interactive seam. Registered unconditionally (a null relay
|
|
1161
1328
|
// answers 503, not 404) and BEFORE the static/SPA fallback below, like every other API route.
|
|
1162
1329
|
registerInteractiveEventRoutes(app, interactiveRelay);
|
|
@@ -1213,6 +1380,11 @@ export async function createServer(adapter, options) {
|
|
|
1213
1380
|
}
|
|
1214
1381
|
return app;
|
|
1215
1382
|
}
|
|
1383
|
+
/** The URL a worker on this host reaches the daemon at: a wildcard bind is reached on loopback. */
|
|
1384
|
+
export function brokerUrlFor(host, port) {
|
|
1385
|
+
const h = host === '0.0.0.0' || host === '' ? '127.0.0.1' : host === '::' ? '::1' : host;
|
|
1386
|
+
return `http://${h.includes(':') ? `[${h}]` : h}:${port}`;
|
|
1387
|
+
}
|
|
1216
1388
|
export async function startServer(adapter, port = 7701, host = '127.0.0.1', options) {
|
|
1217
1389
|
const app = await createServer(adapter, options);
|
|
1218
1390
|
await app.listen({ port, host });
|
|
@@ -1220,6 +1392,10 @@ export async function startServer(adapter, port = 7701, host = '127.0.0.1', opti
|
|
|
1220
1392
|
const boundPort = typeof addr === 'object' && addr ? addr.port : port;
|
|
1221
1393
|
const boundHost = typeof addr === 'object' && addr ? addr.address : host;
|
|
1222
1394
|
app.log.info(`wicked-crew daemon listening on ${boundHost}:${boundPort}`);
|
|
1395
|
+
// DES-MCP-TOOLS-001 S1/S3: the in-process engine hands each governed worker this URL (with its
|
|
1396
|
+
// `WICKED_MCP_TOKEN`) as `WICKED_CREW_URL`, so the garden shim reaches THIS daemon's broker. The
|
|
1397
|
+
// engine reads the daemon's own env at every spawn; a daemon that never listened hands none.
|
|
1398
|
+
process.env['WICKED_CREW_URL'] = brokerUrlFor(boundHost, boundPort);
|
|
1223
1399
|
return { app, port: boundPort, host: boundHost };
|
|
1224
1400
|
}
|
|
1225
1401
|
//# sourceMappingURL=server.js.map
|