wicked-crew 0.7.39 → 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-scope.d.ts +37 -1
- package/dist/api/chat-scope.d.ts.map +1 -1
- package/dist/api/chat-scope.js +131 -7
- package/dist/api/chat-scope.js.map +1 -1
- 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 +647 -59
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +906 -146
- 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/run-timing-index.d.ts +10 -0
- package/dist/api/run-timing-index.d.ts.map +1 -1
- package/dist/api/run-timing-index.js +24 -0
- package/dist/api/run-timing-index.js.map +1 -1
- 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 +316 -116
- 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 +247 -14
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +638 -161
- 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/graph.d.ts +11 -0
- package/dist/projects/graph.d.ts.map +1 -1
- package/dist/projects/graph.js +12 -0
- package/dist/projects/graph.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/skills/phase-skill-gaps.d.ts +108 -0
- package/dist/skills/phase-skill-gaps.d.ts.map +1 -0
- package/dist/skills/phase-skill-gaps.js +199 -0
- package/dist/skills/phase-skill-gaps.js.map +1 -0
- package/dist/skills/runtime.d.ts +1 -1
- package/dist/skills/runtime.d.ts.map +1 -1
- package/dist/skills/runtime.js +2 -1
- package/dist/skills/runtime.js.map +1 -1
- 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 +3307 -897
- 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-BRDAN35i.js +0 -548
- package/dist/studio/assets/index-Drfcsy8b.css +0 -32
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,16 +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 {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import { startInteractiveDemoSubscriber } from '../interactive/demo-events.js';
|
|
26
|
+
import { INTERACTIVE_DRAFT_WORKFLOW_DEF, startInteractiveDraftSubscriber } from '../interactive/draft-events.js';
|
|
27
|
+
import { INTERACTIVE_EDIT_WORKFLOW_DEF, startInteractiveEditSubscriber } from '../interactive/edit-events.js';
|
|
28
|
+
import { INTERACTIVE_CHAT_WORKFLOW_DEF, startInteractiveChatSubscriber } from '../interactive/chat-events.js';
|
|
29
|
+
import { PhaseSkillArming, RunSkillGapIndex } from '../skills/phase-skill-gaps.js';
|
|
29
30
|
import { resolveProjectInteractiveRoot } from '../interactive/bridge-root.js';
|
|
30
31
|
import { sweepDocLedgers } from '../interactive/doc-ledger-sweep.js';
|
|
31
32
|
import { DocRunIndex } from '../interactive/doc-run-index.js';
|
|
@@ -33,8 +34,18 @@ import { TestSetIndex, registerTestSetForRun } from '../qe/test-sets.js';
|
|
|
33
34
|
import { DocGroundingStore } from '../interactive/doc-grounding.js';
|
|
34
35
|
import { ProjectSettingsStore } from '../projects/settings.js';
|
|
35
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';
|
|
36
46
|
import { startProjectBus, MEMBERSHIP_ATTACHED, membershipAttachedKey } from '../projects/events.js';
|
|
37
47
|
import { startInteractiveWsRelay, registerInteractiveEventRoutes } from '../interactive/ws-relay.js';
|
|
48
|
+
import { startTeamWsRelay } from '../team/ws-relay.js';
|
|
38
49
|
import { MembershipIndex } from '../projects/membership-index.js';
|
|
39
50
|
import { writeRunEvidencePointer } from '../projects/charter.js';
|
|
40
51
|
import { engineBenchesUnclassifiedSeats, CoreAdapter } from '../core/adapter.js';
|
|
@@ -47,6 +58,15 @@ import { ChatTranscriptStore } from './chat-transcripts.js';
|
|
|
47
58
|
import { sweepDeliveredWorktree } from './worktree-sweep.js';
|
|
48
59
|
import { installEndpointManifestHook } from './endpoint-manifest.js';
|
|
49
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';
|
|
50
70
|
import { applyWorkerConfigRoot } from './seat-signin.js';
|
|
51
71
|
import { registeredSkillRefs } from '../skills/core-closure.js';
|
|
52
72
|
import { assertSkillsRootFenced } from '../skills/root-fence.js';
|
|
@@ -94,9 +114,11 @@ export async function createServer(adapter, options) {
|
|
|
94
114
|
// test); costs one array push per route at boot, nothing per request.
|
|
95
115
|
app.decorate('endpointManifest', installEndpointManifestHook(app));
|
|
96
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;
|
|
97
120
|
const elicitationCache = new ElicitationCache();
|
|
98
121
|
const terminals = new TerminalHub();
|
|
99
|
-
const qeGateCache = new QeGateCache();
|
|
100
122
|
// Per-seat runtime health (crew#274): folded from the single CoreEvent subscription below,
|
|
101
123
|
// surfaced on GET /roster, recovered by the low-frequency probe armed further down.
|
|
102
124
|
const seatHealth = new SeatHealthTracker({
|
|
@@ -300,10 +322,20 @@ export async function createServer(adapter, options) {
|
|
|
300
322
|
// trail's `guidance.set` entries so notes survive a daemon restart.
|
|
301
323
|
const guidanceIndex = new GuidanceIndex();
|
|
302
324
|
await guidanceIndex.hydrate(audit, (m) => app.log.warn(m));
|
|
325
|
+
// crew#661: each drafting seam's ARM-TIME skill outcome (diagnostics / health), and the runs
|
|
326
|
+
// launched while a seam was unarmed (`session.skill_gaps`) — same durable trail pattern.
|
|
327
|
+
const phaseSkills = new PhaseSkillArming(() => skillsRuntime?.health().current?.gen ?? null);
|
|
328
|
+
const runSkillGaps = new RunSkillGapIndex();
|
|
329
|
+
await runSkillGaps.hydrate(audit, (m) => app.log.warn(m));
|
|
330
|
+
const skillHeld = (name) => skillsRuntime?.holdsSkill(name) ?? false;
|
|
303
331
|
// Delivered-PR record (CREW-UX-8, crew#321): same durable pattern — hydrated from the
|
|
304
332
|
// trail's `run.delivered` entries so `session.delivery` survives a daemon restart.
|
|
305
333
|
const deliveryIndex = new DeliveryIndex();
|
|
306
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));
|
|
307
339
|
// Wave 6 (F-7R2-014): the test sets `qe-author-tests` runs registered — same durable pattern,
|
|
308
340
|
// hydrated from the trail's `testing.testset.registered` entries, fed at each terminal frame.
|
|
309
341
|
const testSets = new TestSetIndex();
|
|
@@ -330,7 +362,7 @@ export async function createServer(adapter, options) {
|
|
|
330
362
|
const deliveryCache = new DeliveryDerivationCache({
|
|
331
363
|
listViews: () => adapter.sessionsDetail(),
|
|
332
364
|
probes: vacuityProbes,
|
|
333
|
-
isDelivered: (runId) => deliveryIndex.
|
|
365
|
+
isDelivered: (runId) => deliveryIndex.isDelivered(runId),
|
|
334
366
|
canDeliver,
|
|
335
367
|
log: (m) => app.log.warn(m),
|
|
336
368
|
// Non-probe derivation throws are defects — error level, so the diagnostics ring sees them.
|
|
@@ -358,7 +390,7 @@ export async function createServer(adapter, options) {
|
|
|
358
390
|
try {
|
|
359
391
|
// Resume/retry re-terminals: already resolved once, and a terminal run's PR URL never
|
|
360
392
|
// changes — never re-read, never double-write the trail.
|
|
361
|
-
if (deliveryIndex.
|
|
393
|
+
if (deliveryIndex.isDelivered(runId))
|
|
362
394
|
return;
|
|
363
395
|
const views = await adapter.sessionsDetail();
|
|
364
396
|
const view = views.find((v) => v.session.id === runId);
|
|
@@ -373,28 +405,35 @@ export async function createServer(adapter, options) {
|
|
|
373
405
|
const output = await adapter.workOutput(coreUnitId(runId, unit));
|
|
374
406
|
if (output === null)
|
|
375
407
|
return;
|
|
376
|
-
|
|
377
|
-
|
|
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)
|
|
378
412
|
return;
|
|
379
413
|
// The durable record first, then the read-side index — the same write order as
|
|
380
414
|
// `guidance.set`, so the index can only LAG a crash (rehydrated at next boot), never
|
|
381
415
|
// hold a record the trail does not.
|
|
382
|
-
audit.record('run.delivered', DAEMON_ACTOR, {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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);
|
|
387
421
|
}
|
|
388
422
|
catch (err) {
|
|
389
423
|
app.log.warn(`[runs] delivery resolution for ${runId} failed (field absent until restart replays the trail): ${err instanceof Error ? err.message : String(err)}`);
|
|
390
424
|
}
|
|
391
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
|
+
};
|
|
392
433
|
const projectBus = options?.projectEvents?.disabled === true
|
|
393
434
|
? null
|
|
394
435
|
: await startProjectBus({
|
|
395
|
-
...(options?.projectEvents?.dbPath
|
|
396
|
-
? { dbPath: options.projectEvents.dbPath }
|
|
397
|
-
: {}),
|
|
436
|
+
...busOf(options?.projectEvents?.dbPath),
|
|
398
437
|
...(options?.projectEvents?.pollIntervalMs !== undefined
|
|
399
438
|
? { pollIntervalMs: options.projectEvents.pollIntervalMs }
|
|
400
439
|
: {}),
|
|
@@ -415,7 +454,6 @@ export async function createServer(adapter, options) {
|
|
|
415
454
|
// is still preferred over its file once armed; the seams themselves arm further down.
|
|
416
455
|
let draftSub = null;
|
|
417
456
|
let editSub = null;
|
|
418
|
-
let demoSub = null;
|
|
419
457
|
let chatSub = null;
|
|
420
458
|
/** The crew-side half of deleting an interactive doc (crew#338): drop the doc's replay-dedup
|
|
421
459
|
* rows from all four handoff ledgers — the draft leg keys by DOCUMENT ID ("one first draft
|
|
@@ -426,11 +464,11 @@ export async function createServer(adapter, options) {
|
|
|
426
464
|
* override, crew#353/#398). Never throws — the report says what happened. */
|
|
427
465
|
const crewStateDir = crewStateHome();
|
|
428
466
|
// F-046: the create-time doc → subject-repo bindings, shared by the proxy (records) and the
|
|
429
|
-
// draft
|
|
467
|
+
// draft seam (read). The store keeps NO file of its own — each binding is a
|
|
430
468
|
// `crew-grounding.json` sidecar beside the doc's `versions.json` under the project's docs root
|
|
431
469
|
// (doc-grounding.ts says why not the state home: core's fence refuses unregistered entries).
|
|
432
470
|
const docGrounding = new DocGroundingStore();
|
|
433
|
-
/** The
|
|
471
|
+
/** The three seams' ledgers, read AT USE TIME (a seam that armed after this closure was built is
|
|
434
472
|
* still preferred over its file) — shared by the doc-delete sweep and the doc↔run index. */
|
|
435
473
|
const docLedgerSources = () => [
|
|
436
474
|
{
|
|
@@ -451,12 +489,6 @@ export async function createServer(adapter, options) {
|
|
|
451
489
|
path: options?.interactiveChatEvents?.ledgerPath ??
|
|
452
490
|
join(crewStateDir, 'interactive-chat-ledger.json'),
|
|
453
491
|
},
|
|
454
|
-
{
|
|
455
|
-
name: 'demo',
|
|
456
|
-
ledger: demoSub?.ledger,
|
|
457
|
-
path: options?.interactiveDemoEvents?.ledgerPath ??
|
|
458
|
-
join(crewStateDir, 'interactive-demo-ledger.json'),
|
|
459
|
-
},
|
|
460
492
|
];
|
|
461
493
|
const dropDocLedgerRows = (documentId) => sweepDocLedgers(documentId, docLedgerSources());
|
|
462
494
|
// Wave 6 (F-4R2-006 root fix): the document ↔ run binding as a direct read off the SAME four
|
|
@@ -470,9 +502,7 @@ export async function createServer(adapter, options) {
|
|
|
470
502
|
const interactiveRelay = options?.interactiveWsRelay?.disabled === true
|
|
471
503
|
? null
|
|
472
504
|
: await startInteractiveWsRelay({
|
|
473
|
-
...(options?.interactiveWsRelay?.dbPath
|
|
474
|
-
? { dbPath: options.interactiveWsRelay.dbPath }
|
|
475
|
-
: {}),
|
|
505
|
+
...busOf(options?.interactiveWsRelay?.dbPath),
|
|
476
506
|
...(options?.interactiveWsRelay?.pollIntervalMs !== undefined
|
|
477
507
|
? { pollIntervalMs: options.interactiveWsRelay.pollIntervalMs }
|
|
478
508
|
: {}),
|
|
@@ -499,6 +529,31 @@ export async function createServer(adapter, options) {
|
|
|
499
529
|
await interactiveRelay.stop();
|
|
500
530
|
});
|
|
501
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
|
+
}
|
|
502
557
|
/** The post-commit half of a project-FILED launch, shared with the launch route (§2.2/§4):
|
|
503
558
|
* the engine already attached the crew.run membership atomically with the launch — here we
|
|
504
559
|
* tag future /ws frames and announce the attach on the project bus. */
|
|
@@ -513,34 +568,16 @@ export async function createServer(adapter, options) {
|
|
|
513
568
|
/** The docs root a project's interactive docs live under — the SAME per-project resolution
|
|
514
569
|
* the project routes and the interactive proxy use (DES-MERGE-001 §7.1/§7.2; partitioned
|
|
515
570
|
* per project since crew#472, with an event that carries no `project_id` belonging to
|
|
516
|
-
* Unfiled). Shared by the edit seam (demo-kind gate
|
|
517
|
-
* 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
|
|
518
572
|
* REAL paths here exactly as the routes check it (crew#474 — one walk, `bridge-root.ts`):
|
|
519
573
|
* a symlinked `projects/<id>` throws `InteractivePartitionRefusedError` into the seam's
|
|
520
574
|
* handler (logged by its `onError`, the event unanswered — fail closed) instead of being
|
|
521
575
|
* followed into another project's docs; the seams only READ under a root the routes
|
|
522
576
|
* materialized, so a missing partition is returned as spelled and nothing is created. */
|
|
523
577
|
const interactiveDocsRoot = (projectId) => resolveProjectInteractiveRoot(projectId, projectId !== undefined ? projectSettings.get(projectId) : null);
|
|
524
|
-
// Arm the opt-in QE gate-event subscription (crew's bus seam). Failure to
|
|
525
|
-
// arm is LOUD but non-fatal: the acceptance route never depends on the bus.
|
|
526
|
-
if (options?.qeGateEvents?.enabled === true) {
|
|
527
|
-
const { dbPath, pollIntervalMs } = options.qeGateEvents;
|
|
528
|
-
const sub = await startQeGateSubscriber(qeGateCache, {
|
|
529
|
-
...(dbPath !== undefined ? { dbPath } : {}),
|
|
530
|
-
...(pollIntervalMs !== undefined ? { pollIntervalMs } : {}),
|
|
531
|
-
log: (m) => app.log.warn(m),
|
|
532
|
-
logError: (m) => app.log.error(m),
|
|
533
|
-
});
|
|
534
|
-
if (sub !== null) {
|
|
535
|
-
app.log.info(`qe gate-event subscription armed (filter wicked.qe.**)`);
|
|
536
|
-
app.addHook('onClose', async () => {
|
|
537
|
-
await sub.stop();
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
578
|
// ── A STUB ENGINE NEVER ANSWERS ANOTHER PRODUCT'S TRAFFIC (crew#309) ────────────────────────
|
|
542
579
|
//
|
|
543
|
-
// The four seams below are ANSWERERS: each
|
|
580
|
+
// The four seams below are ANSWERERS: each taps the bus (core/bus.ts) and replies
|
|
544
581
|
// to wicked-interactive's events by LAUNCHING A GOVERNED RUN. Under `serve --stub` the engine is
|
|
545
582
|
// `Core.spawnStub` — a `StubDispatcher` (every seat votes for the first roster option, no
|
|
546
583
|
// subprocess) plus a `StubStepRunner` (fixed text, no CLI) — so such a run resolves every phase
|
|
@@ -549,7 +586,8 @@ export async function createServer(adapter, options) {
|
|
|
549
586
|
// approved outline — moving on…", "Gate approved the draft…". Nothing ran. Nothing was written.
|
|
550
587
|
// Two gate approvals were announced anyway.
|
|
551
588
|
//
|
|
552
|
-
// 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
|
|
553
591
|
// (`wicked-crew-interactive-draft`), so on the shared bus a stub daemon and the production
|
|
554
592
|
// daemon are ONE consumer group — a frame claimed by the stub is a frame the real daemon never
|
|
555
593
|
// sees. That is what wicked-crew#309 recorded: bus rows 242632–242643 carry a complete draft
|
|
@@ -560,8 +598,8 @@ export async function createServer(adapter, options) {
|
|
|
560
598
|
// convening, which is the StubDispatcher's signature; the real runs 5 minutes later took ~95s to
|
|
561
599
|
// vote and split their seats (`pi` for outline, `claude` for draft).
|
|
562
600
|
//
|
|
563
|
-
// So: refuse to arm, loudly. The deny is scoped to the ANSWERERS on purpose — the
|
|
564
|
-
// 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
|
|
565
603
|
// narrates governance, so neither can fabricate a verdict. Offline/deterministic runs are NOT
|
|
566
604
|
// lost by this: every harness in `e2e/` already does the correct thing, keeping the REAL engine
|
|
567
605
|
// (`stub: false`) and registering a scripted stub SEAT in the roster — which exercises planning,
|
|
@@ -586,7 +624,7 @@ export async function createServer(adapter, options) {
|
|
|
586
624
|
if (options?.interactiveDraftEvents?.enabled === true && !refuseStubSeam('interactive-draft')) {
|
|
587
625
|
const o = options.interactiveDraftEvents;
|
|
588
626
|
draftSub = await startInteractiveDraftSubscriber(adapter, {
|
|
589
|
-
...(o.dbPath
|
|
627
|
+
...busOf(o.dbPath),
|
|
590
628
|
...(o.pollIntervalMs !== undefined ? { pollIntervalMs: o.pollIntervalMs } : {}),
|
|
591
629
|
...(o.heartbeatMs !== undefined ? { heartbeatMs: o.heartbeatMs } : {}),
|
|
592
630
|
...(o.ledgerPath !== undefined ? { ledgerPath: o.ledgerPath } : {}),
|
|
@@ -594,8 +632,9 @@ export async function createServer(adapter, options) {
|
|
|
594
632
|
...(o.clisJson !== undefined ? { clisJson: o.clisJson } : {}),
|
|
595
633
|
// The roster WITH standing when no override is set (F-RECON-002/003).
|
|
596
634
|
roster: rosterWithStanding,
|
|
597
|
-
// The quality-floor skill gate (draft-skill.ts): stamped only when the published snapshot holds it
|
|
598
|
-
|
|
635
|
+
// The quality-floor skill gate (draft-skill.ts): stamped only when the published snapshot holds it;
|
|
636
|
+
// the arm-time answer is recorded for /diagnostics (crew#661).
|
|
637
|
+
skillHeld: phaseSkills.probe('interactive-draft', INTERACTIVE_DRAFT_WORKFLOW_DEF, skillHeld),
|
|
599
638
|
onRunFiled: fileRun,
|
|
600
639
|
onRunLaunched: (runId, detail) => { recordRunLaunched(audit, runTimingIndex, DAEMON_ACTOR, runId, detail); },
|
|
601
640
|
// F-046: the create-time grounding sidecar is read under the SAME per-project docs root the
|
|
@@ -605,6 +644,9 @@ export async function createServer(adapter, options) {
|
|
|
605
644
|
log: (m) => app.log.warn(m),
|
|
606
645
|
logError: (m) => app.log.error(m),
|
|
607
646
|
});
|
|
647
|
+
// crew#661: a seam that failed to arm runs nothing — its arm-time skill answer is not a gap.
|
|
648
|
+
if (draftSub === null)
|
|
649
|
+
phaseSkills.forget('interactive-draft');
|
|
608
650
|
if (draftSub !== null) {
|
|
609
651
|
const sub = draftSub;
|
|
610
652
|
app.log.info('interactive-draft subscription armed (filter wicked.interactive.doc.created)');
|
|
@@ -616,14 +658,10 @@ export async function createServer(adapter, options) {
|
|
|
616
658
|
// Arm the opt-in interactive STRUCTURAL-edit answering seam (task #86 final leg). Same
|
|
617
659
|
// posture as the draft seam: failure to arm is LOUD but non-fatal — interactive's assist
|
|
618
660
|
// loop is the fallback answerer, and this daemon must boot on a machine whose bus is broken.
|
|
619
|
-
// `demoSub` (declared above) is deliberately still null here: the demo seam arms after (it
|
|
620
|
-
// must, so the per-doc serialization contract can see the edit seam's in-flight set), but the
|
|
621
|
-
// edit seam's demo-kind gate needs to know at EVENT time whether the demo seam actually came
|
|
622
|
-
// up — a closure over the binding reads the settled value; a boolean captured here would not.
|
|
623
661
|
if (options?.interactiveEditEvents?.enabled === true && !refuseStubSeam('interactive-edit')) {
|
|
624
662
|
const o = options.interactiveEditEvents;
|
|
625
663
|
editSub = await startInteractiveEditSubscriber(adapter, {
|
|
626
|
-
...(o.dbPath
|
|
664
|
+
...busOf(o.dbPath),
|
|
627
665
|
...(o.pollIntervalMs !== undefined ? { pollIntervalMs: o.pollIntervalMs } : {}),
|
|
628
666
|
...(o.heartbeatMs !== undefined ? { heartbeatMs: o.heartbeatMs } : {}),
|
|
629
667
|
...(o.ledgerPath !== undefined ? { ledgerPath: o.ledgerPath } : {}),
|
|
@@ -631,17 +669,19 @@ export async function createServer(adapter, options) {
|
|
|
631
669
|
...(o.clisJson !== undefined ? { clisJson: o.clisJson } : {}),
|
|
632
670
|
// The roster WITH standing when no override is set (F-RECON-002/003).
|
|
633
671
|
roster: rosterWithStanding,
|
|
634
|
-
// The quality-floor skill gate (draft-skill.ts): stamped only when the published snapshot holds it
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
//
|
|
638
|
-
//
|
|
672
|
+
// The quality-floor skill gate (draft-skill.ts): stamped only when the published snapshot holds it;
|
|
673
|
+
// the arm-time answer is recorded for /diagnostics (crew#661).
|
|
674
|
+
skillHeld: phaseSkills.probe('interactive-edit', INTERACTIVE_EDIT_WORKFLOW_DEF, skillHeld),
|
|
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).
|
|
639
677
|
resolveDocsRoot: o.resolveDocsRoot ?? interactiveDocsRoot,
|
|
640
|
-
demoSeamArmed: () => demoSub !== null,
|
|
641
678
|
onRunFiled: fileRun,
|
|
642
679
|
log: (m) => app.log.warn(m),
|
|
643
680
|
logError: (m) => app.log.error(m),
|
|
644
681
|
});
|
|
682
|
+
// crew#661: a seam that failed to arm runs nothing — its arm-time skill answer is not a gap.
|
|
683
|
+
if (editSub === null)
|
|
684
|
+
phaseSkills.forget('interactive-edit');
|
|
645
685
|
if (editSub !== null) {
|
|
646
686
|
const sub = editSub;
|
|
647
687
|
app.log.info('interactive-edit subscription armed (filter wicked.interactive.feedback.processed)');
|
|
@@ -650,37 +690,6 @@ export async function createServer(adapter, options) {
|
|
|
650
690
|
});
|
|
651
691
|
}
|
|
652
692
|
}
|
|
653
|
-
// Arm the opt-in interactive DEMO answering seam (CREW-UX-9 — the retired assist agent's
|
|
654
|
-
// Step 8). Same posture as the sibling seams: failure to arm is LOUD but non-fatal — a demo
|
|
655
|
-
// doc then keeps its placeholder, and this daemon must boot on a machine whose bus is
|
|
656
|
-
// broken. Armed beside the edit seam because the two split `feedback.processed` by the doc
|
|
657
|
-
// manifest's kind (demo → re-author the spec here; everything else → structural edit there).
|
|
658
|
-
if (options?.interactiveDemoEvents?.enabled === true && !refuseStubSeam('interactive-demo')) {
|
|
659
|
-
const o = options.interactiveDemoEvents;
|
|
660
|
-
demoSub = await startInteractiveDemoSubscriber(adapter, {
|
|
661
|
-
...(o.dbPath !== undefined ? { dbPath: o.dbPath } : {}),
|
|
662
|
-
...(o.pollIntervalMs !== undefined ? { pollIntervalMs: o.pollIntervalMs } : {}),
|
|
663
|
-
...(o.heartbeatMs !== undefined ? { heartbeatMs: o.heartbeatMs } : {}),
|
|
664
|
-
...(o.ledgerPath !== undefined ? { ledgerPath: o.ledgerPath } : {}),
|
|
665
|
-
...(o.demoDir !== undefined ? { demoDir: o.demoDir } : {}),
|
|
666
|
-
...(o.clisJson !== undefined ? { clisJson: o.clisJson } : {}),
|
|
667
|
-
// The roster WITH standing when no override is set (F-RECON-002/003).
|
|
668
|
-
roster: rosterWithStanding,
|
|
669
|
-
resolveDocsRoot: o.resolveDocsRoot ?? interactiveDocsRoot,
|
|
670
|
-
onRunFiled: fileRun,
|
|
671
|
-
onRunLaunched: (runId, detail) => { recordRunLaunched(audit, runTimingIndex, DAEMON_ACTOR, runId, detail); },
|
|
672
|
-
groundingStore: docGrounding,
|
|
673
|
-
log: (m) => app.log.warn(m),
|
|
674
|
-
logError: (m) => app.log.error(m),
|
|
675
|
-
});
|
|
676
|
-
if (demoSub !== null) {
|
|
677
|
-
const sub = demoSub;
|
|
678
|
-
app.log.info('interactive-demo subscription armed (filters wicked.interactive.doc.created + feedback.processed, kind:demo)');
|
|
679
|
-
app.addHook('onClose', async () => {
|
|
680
|
-
await sub.stop();
|
|
681
|
-
});
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
693
|
// Arm the opt-in interactive CHAT answering seam (CREW-UX-5 — the iteration ask). Same
|
|
685
694
|
// posture again: failure to arm is LOUD but non-fatal. Armed AFTER the sibling seams so the
|
|
686
695
|
// per-doc serialization contract (no draft/edit/chat run races another on one doc) can
|
|
@@ -689,7 +698,7 @@ export async function createServer(adapter, options) {
|
|
|
689
698
|
if (options?.interactiveChatEvents?.enabled === true && !refuseStubSeam('interactive-chat')) {
|
|
690
699
|
const o = options.interactiveChatEvents;
|
|
691
700
|
chatSub = await startInteractiveChatSubscriber(adapter, {
|
|
692
|
-
...(o.dbPath
|
|
701
|
+
...busOf(o.dbPath),
|
|
693
702
|
...(o.pollIntervalMs !== undefined ? { pollIntervalMs: o.pollIntervalMs } : {}),
|
|
694
703
|
...(o.heartbeatMs !== undefined ? { heartbeatMs: o.heartbeatMs } : {}),
|
|
695
704
|
...(o.ledgerPath !== undefined ? { ledgerPath: o.ledgerPath } : {}),
|
|
@@ -697,8 +706,9 @@ export async function createServer(adapter, options) {
|
|
|
697
706
|
...(o.clisJson !== undefined ? { clisJson: o.clisJson } : {}),
|
|
698
707
|
// The roster WITH standing when no override is set (F-RECON-002/003).
|
|
699
708
|
roster: rosterWithStanding,
|
|
700
|
-
// The quality-floor skill gate (draft-skill.ts): stamped only when the published snapshot holds it
|
|
701
|
-
|
|
709
|
+
// The quality-floor skill gate (draft-skill.ts): stamped only when the published snapshot holds it;
|
|
710
|
+
// the arm-time answer is recorded for /diagnostics (crew#661).
|
|
711
|
+
skillHeld: phaseSkills.probe('interactive-chat', INTERACTIVE_CHAT_WORKFLOW_DEF, skillHeld),
|
|
702
712
|
...(o.queueSweepMs !== undefined ? { queueSweepMs: o.queueSweepMs } : {}),
|
|
703
713
|
...(o.landingGateMs !== undefined ? { landingGateMs: o.landingGateMs } : {}),
|
|
704
714
|
resolveDocsRoot: o.resolveDocsRoot ?? interactiveDocsRoot,
|
|
@@ -708,6 +718,9 @@ export async function createServer(adapter, options) {
|
|
|
708
718
|
log: (m) => app.log.warn(m),
|
|
709
719
|
logError: (m) => app.log.error(m),
|
|
710
720
|
});
|
|
721
|
+
// crew#661: a seam that failed to arm runs nothing — its arm-time skill answer is not a gap.
|
|
722
|
+
if (chatSub === null)
|
|
723
|
+
phaseSkills.forget('interactive-chat');
|
|
711
724
|
if (chatSub !== null) {
|
|
712
725
|
const sub = chatSub;
|
|
713
726
|
app.log.info('interactive-chat subscription armed (filter wicked.interactive.chat.posted)');
|
|
@@ -734,14 +747,23 @@ export async function createServer(adapter, options) {
|
|
|
734
747
|
// candidates. Views with no units (older engines, stub adapters) keep the historical
|
|
735
748
|
// `unit_ix` fallback.
|
|
736
749
|
const cursor = resolveCursorUnit(v);
|
|
737
|
-
//
|
|
738
|
-
//
|
|
739
|
-
//
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
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);
|
|
745
767
|
return {
|
|
746
768
|
id: v.session.id,
|
|
747
769
|
ord: cursor?.ord ?? v.session.unit_ix,
|
|
@@ -801,6 +823,13 @@ export async function createServer(adapter, options) {
|
|
|
801
823
|
onTurnTimeout: ({ session, ...detail }) => {
|
|
802
824
|
audit.record('run.turn.timedout', { id: 'stall-watchdog', kind: 'system', trust: 'admin' }, { runId: session, detail });
|
|
803
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
|
+
}),
|
|
804
833
|
log: (m) => app.log.warn(m),
|
|
805
834
|
});
|
|
806
835
|
// The daemon's single CoreEvent subscription fans out here: cache gate prompts
|
|
@@ -827,6 +856,51 @@ export async function createServer(adapter, options) {
|
|
|
827
856
|
// written from the stamped frames below, dropped with the chat on `chatClosed`, served on
|
|
828
857
|
// `GET /chats/:id.messages`.
|
|
829
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
|
+
}
|
|
830
904
|
// crew#619: see declaration of chatRetained/runToChat above (before the hydration block).
|
|
831
905
|
// linkChatRun populates both maps and is called from the hydration block (function-hoisted)
|
|
832
906
|
// and from the launch route (via RoutesRuntime.linkChatRun).
|
|
@@ -889,6 +963,13 @@ export async function createServer(adapter, options) {
|
|
|
889
963
|
const session = typeof event.session === 'string' ? event.session : undefined;
|
|
890
964
|
const projectId = session !== undefined ? membershipIndex.projectOf(session) : undefined;
|
|
891
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);
|
|
892
973
|
// The delivered-PR record (CREW-UX-8, crew#321): resolved once per run at its terminal
|
|
893
974
|
// frame, best-effort, off the hot path — see `resolveRunDelivery` above for why BOTH
|
|
894
975
|
// terminal frames trigger it and why a failed deliver is a no-op. THEN the delivery-
|
|
@@ -958,7 +1039,6 @@ export async function createServer(adapter, options) {
|
|
|
958
1039
|
adapter,
|
|
959
1040
|
audit,
|
|
960
1041
|
index: testSets,
|
|
961
|
-
workflows: () => adapter.listWorkflows(),
|
|
962
1042
|
deliveryUrlFor: (runId) => deliveryIndex.urlFor(runId),
|
|
963
1043
|
labelFor: (runId) => {
|
|
964
1044
|
const attach = groupIndex.attachOf(runId);
|
|
@@ -1011,6 +1091,9 @@ export async function createServer(adapter, options) {
|
|
|
1011
1091
|
// close like the event listener.
|
|
1012
1092
|
const offLaunch = adapter.onLaunch((notice) => {
|
|
1013
1093
|
skillsRuntime?.launched(notice);
|
|
1094
|
+
// crew#661: a run the engine ACCEPTED on a workflow that armed WITHOUT its declared skill proceeds
|
|
1095
|
+
// degraded and says so on the run (`session.skill_gaps`, a `run.skill.unarmed` trail entry). Never throws.
|
|
1096
|
+
runSkillGaps.onLaunch(notice, phaseSkills, audit, DAEMON_ACTOR, (m) => app.log.warn(m));
|
|
1014
1097
|
});
|
|
1015
1098
|
app.addHook('onClose', async () => {
|
|
1016
1099
|
offEvent();
|
|
@@ -1058,14 +1141,17 @@ export async function createServer(adapter, options) {
|
|
|
1058
1141
|
try {
|
|
1059
1142
|
done(null, JSON.parse(body));
|
|
1060
1143
|
}
|
|
1061
|
-
catch
|
|
1144
|
+
catch {
|
|
1062
1145
|
// A syntactically invalid body is the CLIENT's error: without an explicit statusCode
|
|
1063
1146
|
// Fastify reports a parser error as a 500, which misfiles "you sent `{not json`" as a
|
|
1064
1147
|
// server fault — on the elicitation answer path that told an operator the daemon broke
|
|
1065
1148
|
// when their request did. 400 matches Fastify's own default JSON parser
|
|
1066
1149
|
// (FST_ERR_CTP_INVALID_JSON) and every schema-level 400 these routes already return.
|
|
1067
|
-
|
|
1068
|
-
|
|
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' }));
|
|
1069
1155
|
}
|
|
1070
1156
|
});
|
|
1071
1157
|
await app.register(fastifyWebsocket);
|
|
@@ -1087,13 +1173,47 @@ export async function createServer(adapter, options) {
|
|
|
1087
1173
|
// diagnostics reads the bundle's shipped version manifest from the same root the static
|
|
1088
1174
|
// handler serves. One resolution, two consumers.
|
|
1089
1175
|
const studioRoot = options?.studioRoot ?? defaultStudioRoot();
|
|
1090
|
-
|
|
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, {
|
|
1091
1210
|
bus: projectBus,
|
|
1092
1211
|
index: membershipIndex,
|
|
1093
1212
|
log: (m) => app.log.warn(m),
|
|
1094
1213
|
settings: projectSettings,
|
|
1095
1214
|
}, { audit, authMode: auth.mode }, {
|
|
1096
1215
|
seatHealth,
|
|
1216
|
+
deliveryFreeze,
|
|
1097
1217
|
// wicked-studio#284: the watchdog's remembered frames ride `GET /runs/:id/events`.
|
|
1098
1218
|
stallFrames: (runId) => stallFrameIndex.framesFor(runId),
|
|
1099
1219
|
// The SAME standing accessor the seams and the adapter launch with (F-RECON-002/003).
|
|
@@ -1102,6 +1222,8 @@ export async function createServer(adapter, options) {
|
|
|
1102
1222
|
groupIndex,
|
|
1103
1223
|
runTimingIndex,
|
|
1104
1224
|
guidanceIndex,
|
|
1225
|
+
phaseSkills,
|
|
1226
|
+
runSkillGaps,
|
|
1105
1227
|
chatScopes,
|
|
1106
1228
|
chatTurns,
|
|
1107
1229
|
chatTranscripts,
|
|
@@ -1125,6 +1247,13 @@ export async function createServer(adapter, options) {
|
|
|
1125
1247
|
interactiveBridgeBusDataDir: options?.interactiveBridge?.busDataDir ?? null,
|
|
1126
1248
|
docGrounding,
|
|
1127
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,
|
|
1128
1257
|
// wicked-core#411 / crew#497: the live state-home classification the routes report and gate on.
|
|
1129
1258
|
stateHome: stateHomeWatch,
|
|
1130
1259
|
// Routes that say something to the thread (a refused chat seat, F-2R2-007) emit through the
|
|
@@ -1133,6 +1262,68 @@ export async function createServer(adapter, options) {
|
|
|
1133
1262
|
// crew#619: retain a chat's transcript for the lifetime of its promoted run.
|
|
1134
1263
|
linkChatRun,
|
|
1135
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));
|
|
1136
1327
|
// The UI-emittable direction of the interactive seam. Registered unconditionally (a null relay
|
|
1137
1328
|
// answers 503, not 404) and BEFORE the static/SPA fallback below, like every other API route.
|
|
1138
1329
|
registerInteractiveEventRoutes(app, interactiveRelay);
|
|
@@ -1189,6 +1380,11 @@ export async function createServer(adapter, options) {
|
|
|
1189
1380
|
}
|
|
1190
1381
|
return app;
|
|
1191
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
|
+
}
|
|
1192
1388
|
export async function startServer(adapter, port = 7701, host = '127.0.0.1', options) {
|
|
1193
1389
|
const app = await createServer(adapter, options);
|
|
1194
1390
|
await app.listen({ port, host });
|
|
@@ -1196,6 +1392,10 @@ export async function startServer(adapter, port = 7701, host = '127.0.0.1', opti
|
|
|
1196
1392
|
const boundPort = typeof addr === 'object' && addr ? addr.port : port;
|
|
1197
1393
|
const boundHost = typeof addr === 'object' && addr ? addr.address : host;
|
|
1198
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);
|
|
1199
1399
|
return { app, port: boundPort, host: boundHost };
|
|
1200
1400
|
}
|
|
1201
1401
|
//# sourceMappingURL=server.js.map
|