wicked-crew 0.16.12 → 0.16.13

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.
Files changed (89) hide show
  1. package/README.md +2 -1
  2. package/dist/api/chat-history.d.ts +6 -0
  3. package/dist/api/chat-history.d.ts.map +1 -1
  4. package/dist/api/chat-history.js +21 -0
  5. package/dist/api/chat-history.js.map +1 -1
  6. package/dist/api/chat-proposals.d.ts +61 -6
  7. package/dist/api/chat-proposals.d.ts.map +1 -1
  8. package/dist/api/chat-proposals.js +252 -24
  9. package/dist/api/chat-proposals.js.map +1 -1
  10. package/dist/api/routes.d.ts +14 -2
  11. package/dist/api/routes.d.ts.map +1 -1
  12. package/dist/api/routes.js +119 -9
  13. package/dist/api/routes.js.map +1 -1
  14. package/dist/api/run-timing-index.d.ts +18 -0
  15. package/dist/api/run-timing-index.d.ts.map +1 -1
  16. package/dist/api/run-timing-index.js +34 -0
  17. package/dist/api/run-timing-index.js.map +1 -1
  18. package/dist/api/server.d.ts.map +1 -1
  19. package/dist/api/server.js +100 -1
  20. package/dist/api/server.js.map +1 -1
  21. package/dist/apps/spec.d.ts +3009 -0
  22. package/dist/apps/spec.d.ts.map +1 -0
  23. package/dist/apps/spec.js +870 -0
  24. package/dist/apps/spec.js.map +1 -0
  25. package/dist/cli/index.js +3 -3
  26. package/dist/cli/index.js.map +1 -1
  27. package/dist/core/run-boot-resume.d.ts +4 -1
  28. package/dist/core/run-boot-resume.d.ts.map +1 -1
  29. package/dist/core/run-boot-resume.js +6 -1
  30. package/dist/core/run-boot-resume.js.map +1 -1
  31. package/dist/editors/csp.d.ts +24 -4
  32. package/dist/editors/csp.d.ts.map +1 -1
  33. package/dist/editors/csp.js +35 -8
  34. package/dist/editors/csp.js.map +1 -1
  35. package/dist/editors/routes.d.ts +4 -1
  36. package/dist/editors/routes.d.ts.map +1 -1
  37. package/dist/editors/routes.js +30 -3
  38. package/dist/editors/routes.js.map +1 -1
  39. package/dist/editors/webrtc-guard.d.ts +94 -0
  40. package/dist/editors/webrtc-guard.d.ts.map +1 -0
  41. package/dist/editors/webrtc-guard.js +506 -0
  42. package/dist/editors/webrtc-guard.js.map +1 -0
  43. package/dist/interactive/bridge-pool.d.ts +11 -2
  44. package/dist/interactive/bridge-pool.d.ts.map +1 -1
  45. package/dist/interactive/bridge-pool.js +10 -1
  46. package/dist/interactive/bridge-pool.js.map +1 -1
  47. package/dist/interactive/doc-binding.d.ts +156 -0
  48. package/dist/interactive/doc-binding.d.ts.map +1 -0
  49. package/dist/interactive/doc-binding.js +277 -0
  50. package/dist/interactive/doc-binding.js.map +1 -0
  51. package/dist/interactive/doc-delete-routes.d.ts +3 -0
  52. package/dist/interactive/doc-delete-routes.d.ts.map +1 -1
  53. package/dist/interactive/doc-delete-routes.js +4 -0
  54. package/dist/interactive/doc-delete-routes.js.map +1 -1
  55. package/dist/interactive/draft-events.d.ts +89 -3
  56. package/dist/interactive/draft-events.d.ts.map +1 -1
  57. package/dist/interactive/draft-events.js +438 -27
  58. package/dist/interactive/draft-events.js.map +1 -1
  59. package/dist/interactive/edit-events.d.ts +2 -0
  60. package/dist/interactive/edit-events.d.ts.map +1 -1
  61. package/dist/interactive/edit-events.js +11 -0
  62. package/dist/interactive/edit-events.js.map +1 -1
  63. package/dist/interactive/proposal-create.d.ts +143 -0
  64. package/dist/interactive/proposal-create.d.ts.map +1 -0
  65. package/dist/interactive/proposal-create.js +318 -0
  66. package/dist/interactive/proposal-create.js.map +1 -0
  67. package/dist/interactive/proxy-routes.d.ts +60 -3
  68. package/dist/interactive/proxy-routes.d.ts.map +1 -1
  69. package/dist/interactive/proxy-routes.js +209 -3
  70. package/dist/interactive/proxy-routes.js.map +1 -1
  71. package/dist/interactive/source-packet.d.ts +126 -0
  72. package/dist/interactive/source-packet.d.ts.map +1 -0
  73. package/dist/interactive/source-packet.js +482 -0
  74. package/dist/interactive/source-packet.js.map +1 -0
  75. package/dist/studio/assets/{index-C_UIgTiy.js → index-u4O_7N-b.js} +1 -1
  76. package/dist/studio/editors/wicked-deck/editor.json +1 -1
  77. package/dist/studio/editors/wicked-deck/index.html +1 -1
  78. package/dist/studio/editors/wicked-doc/editor.json +1 -1
  79. package/dist/studio/editors/wicked-doc/index.html +1 -1
  80. package/dist/studio/editors/wicked-insights/editor.json +1 -1
  81. package/dist/studio/editors/wicked-insights/index.html +1 -1
  82. package/dist/studio/editors/wicked-page/editor.json +1 -1
  83. package/dist/studio/editors/wicked-page/index.html +1 -1
  84. package/dist/studio/editors/wicked-video/editor.json +1 -1
  85. package/dist/studio/editors/wicked-video/index.html +1 -1
  86. package/dist/studio/index.html +1 -1
  87. package/dist/studio/testid-inventory.json +1 -1
  88. package/endpoint-manifest.json +1 -1
  89. package/package.json +4 -3
@@ -6,9 +6,9 @@ import { z } from 'zod';
6
6
  import { listRequirementsRead, getRequirement, patchRequirement, readRequirementsGraph, REQUIREMENTS_NOT_GENERATED, } from './requirements.js';
7
7
  import { randomUUID } from 'node:crypto';
8
8
  import { homedir } from 'node:os';
9
- import { readFileSync, existsSync, mkdirSync } from 'node:fs';
9
+ import { readFileSync, existsSync, mkdirSync, realpathSync } from 'node:fs';
10
10
  import { promises as fsp } from 'node:fs';
11
- import { isAbsolute, join, relative, resolve } from 'node:path';
11
+ import { basename, isAbsolute, join, relative, resolve } from 'node:path';
12
12
  import { fileURLToPath } from 'node:url';
13
13
  import { videoPalette } from '../appearance/video-palette.js';
14
14
  import { CampaignsUnsupportedError, ChatUnsupportedError, CoreAdapter, ElicitationUnsupportedError, GovernanceReplayUnsupportedError, PlanLaunchUnsupportedError, SteeringUnsupportedError, addonSupportsQeOverride, addonSupportsReducedAssurance, humanGatePhaseIds, settingsFilePath } from '../core/adapter.js';
@@ -105,8 +105,13 @@ import { deliverIdentityFor, deliverRepoFor, deliverTargetView, isGitHubLogin, p
105
105
  import { credentialsMissingIn, deliverCredentialsProbe, missingCredentials } from '../core/deliver-credentials.js';
106
106
  import { codebaseArchiveUrl } from './codebase-archive.js';
107
107
  import { projectRepoCandidates } from '../interactive/doc-grounding.js';
108
- import { artifactStatus, beneathSupported, buildTurnContext, chatMount, ChatMessageContextSchema, contextReadRoots, ensureRealDir, parseArtifactRef, readGroundingBeneath, seatPathOf, UNFILED_MOUNT, } from './chat-context.js';
109
- import { registerInteractiveProxy } from '../interactive/proxy-routes.js';
108
+ import { artifactStatus, beneathSupported, buildTurnContext, chatMount, ChatMessageContextSchema, contextReadRoots, ensureRealDir, sha256Hex, parseArtifactRef, readGroundingBeneath, seatPathOf, UNFILED_MOUNT, } from './chat-context.js';
109
+ import { createDocInProcess, registerInteractiveProxy } from '../interactive/proxy-routes.js';
110
+ import { CreateLauncher } from '../interactive/proposal-create.js';
111
+ import { buildRunBrief } from './run-brief.js';
112
+ import { readBeneath } from './beneath.js';
113
+ /** A context brief copied into a document's packet, at most (the packet's per-file cap). */
114
+ const BRIEF_COPY_MAX = 512 * 1024;
110
115
  import { registerInteractiveDocDelete } from '../interactive/doc-delete-routes.js';
111
116
  import { registerInteractiveDocChecks } from '../interactive/doc-checks-routes.js';
112
117
  import { crewStateHome, stateHomeOfDb } from '../projects/state-home.js';
@@ -928,15 +933,20 @@ runtime = {}) {
928
933
  // off — each later slice turns its own on and registers its launcher. The TEST configuration (§13 A2) turns every
929
934
  // operation's resolution on and registers a recording stub that starts nothing.
930
935
  const chatArtifactsStub = runtime.chatArtifactsStub === true;
936
+ // A3: a real `create` exists whenever the draft seam can launch a proposal-bound draft (the launcher is registered
937
+ // below, once the bridge pool exists). Production advertises it; the test configuration keeps the recording stub only
938
+ // for the operations whose slices have not landed.
939
+ const realCreate = runtime.docBindings !== undefined && runtime.launchBoundDraft !== undefined;
940
+ const stubOps = realCreate ? ['refine', 'theme', 'demo'] : ['create', 'refine', 'theme', 'demo'];
931
941
  const chatArtifactFlags = chatArtifactsStub
932
- ? { context: true, proposals: true, create: true, refine: true, theme: true, demo: true, stub: true }
933
- : { context: true, proposals: true, create: false, refine: false, theme: false, demo: false };
942
+ ? { context: true, proposals: true, create: true, refine: true, theme: true, demo: true, stub: true, stubOps }
943
+ : { context: true, proposals: true, create: realCreate, refine: false, theme: false, demo: false };
934
944
  const proposalLaunchers = new LaunchOnceRegistry();
935
945
  if (chatArtifactsStub) {
936
946
  // Under the registered `chats` entry (a new top-level state-home entry would make core refuse every launch), and
937
947
  // not a `.jsonl` there (the boot sweep clears orphaned transcripts by that suffix).
938
948
  const stub = new RecordingStubDispatcher(join(crewStateHome(), 'chats', 'proposal-stub', 'dispatches.ndjson'));
939
- for (const kind of ['create', 'refine', 'theme', 'demo'])
949
+ for (const kind of stubOps)
940
950
  proposalLaunchers.register(kind, stub);
941
951
  }
942
952
  const deliveryIndex = runtime.deliveryIndex ?? new DeliveryIndex();
@@ -1247,6 +1257,15 @@ runtime = {}) {
1247
1257
  const chatId = runTimingIndex.chatIdFor(view.session.id);
1248
1258
  if (chatId !== undefined)
1249
1259
  view.session.chat_id = chatId;
1260
+ // DES-CHAT-ARTIFACTS-001 (A3): the proposal a run answers, the artifact it makes, and a landing that failed.
1261
+ const proposalOf = runTimingIndex.proposalOf(view.session.id);
1262
+ if (proposalOf !== undefined) {
1263
+ view.session.proposal_id = proposalOf.proposalId;
1264
+ view.session.artifact_ref = proposalOf.artifactRef;
1265
+ }
1266
+ const landingFailed = runTimingIndex.landingFailedOf(view.session.id);
1267
+ if (landingFailed !== undefined)
1268
+ view.session.landing_failed = landingFailed;
1250
1269
  // crew#552: the onboarding run a chained capture was launched from — ABSENT otherwise.
1251
1270
  const chainedFrom = runTimingIndex.chainedFromOf(view.session.id);
1252
1271
  if (chainedFrom !== undefined)
@@ -2452,6 +2471,13 @@ runtime = {}) {
2452
2471
  }
2453
2472
  deliver = 'none';
2454
2473
  }
2474
+ // DES-CHAT-ARTIFACTS-001 §4.3 (A3): one live job per chat, work and artifact runs together. A work launch filed under
2475
+ // a chat whose proposal's document attempt has no outcome yet is refused, as Go is refused the other way round.
2476
+ if (b.chatId !== undefined && chatArtifactFlags.proposals) {
2477
+ const live = await chatProposals.liveJobOf(b.chatId).catch(() => null);
2478
+ if (live !== null)
2479
+ return reply.code(409).send({ code: 'chat_busy', error: `This chat already has a job running (${live}). Wait, or stop that run.` });
2480
+ }
2455
2481
  try {
2456
2482
  const runId = await adapter.launchRun(input);
2457
2483
  // crew#641/#642 (item 5): when a run is promoted from a chat, record how many seats
@@ -3698,7 +3724,9 @@ runtime = {}) {
3698
3724
  isAskPath: (runId) => runTimingIndex.isAskPath(runId),
3699
3725
  docsRoot: (projectId) => projectDocsRoot(adapter, projectSettings, projectId),
3700
3726
  projectName: async (projectId) => (await adapter.projectGet(projectId).catch(() => null))?.name ?? null,
3701
- attachments: (chatId) => chatAttachments.list(chatId).map((a) => ({ id: a.id, name: a.name })),
3727
+ projectRepos: async (projectId) => (await projectRepoCandidates(adapter, projectId, (m) => app.log.warn(m))).map((r) => ({ id: r.repoRef, name: r.name })),
3728
+ attachments: (chatId) => chatAttachments.list(chatId).map((a) => ({ id: a.id, name: a.name, bytes: a.bytes, mediaType: a.mediaType })),
3729
+ repoIds: async () => new Set((await adapter.listRepos()).map((r) => r.id)),
3702
3730
  workflowSteps: async (name, projectId) => {
3703
3731
  // The preset a launch in this project resolves first (a project row wins over a global one), then the def.
3704
3732
  const presets = await adapter.listPresets(projectId ?? undefined).catch(() => []);
@@ -5219,6 +5247,15 @@ runtime = {}) {
5219
5247
  // but its work is complete and liftable — normalize to `completed` so the terminal branch
5220
5248
  // below refuses resume with the DELIVER recovery (POST /runs/:id/deliver), not a re-entry.
5221
5249
  const conflictStrand = normalizeStranded(run);
5250
+ // DES-CHAT-ARTIFACTS-001 §6.2 (A3): a chat proposal's FAILED attempt is never resumed from its cursor: its seam flight
5251
+ // is gone (a resume would finish without landing anything) and its retry may already run. Try again is the path.
5252
+ // A still-live bound run's own gate is approved as any run's.
5253
+ if (run.session.status === 'failed' && runtime.docBindings?.isBound(id) === true) {
5254
+ return reply.code(409).send({
5255
+ error: `run ${id} answered a chat proposal: it cannot be resumed. Use Try again on its card in the chat (a new attempt at the same document).`,
5256
+ recovery: 'retry',
5257
+ });
5258
+ }
5222
5259
  // A TERMINAL run is refused LOUDLY, with the actual recovery named (crew#311 defect 2).
5223
5260
  // The engine's `resume_run` no-ops on completed/cancelled and answers the status token, so
5224
5261
  // this route used to reply 200 {"status":"cancelled"} — on the exact runs an operator was
@@ -8676,14 +8713,80 @@ runtime = {}) {
8676
8713
  toReview: toReview.kind === 'ok' ? toReview.value : null,
8677
8714
  });
8678
8715
  });
8716
+ // DES-CHAT-ARTIFACTS-001 §5.2 (A3): a create's `chat_id` + `proposal_id` counts only through the caller's accepted record.
8717
+ const proposalGrant = (chatId, pid, actorId) => (CHAT_ID.test(chatId) ? chatProposals.grantFor(chatId, pid, actorId) : null);
8679
8718
  registerInteractiveProxy(app, adapter, {
8680
8719
  settings: projectSettings,
8681
8720
  pool: interactiveBridges,
8682
8721
  ...(runtime.docGrounding !== undefined ? { grounding: runtime.docGrounding } : {}),
8683
8722
  // crew#631: supply the standing roster so doc/demo creates can refuse an unavailable seat.
8684
8723
  roster: () => rosterWithStanding(),
8724
+ proposalGrant,
8725
+ actorOf: (req) => actorOf(req).id,
8685
8726
  log: (m) => app.log.warn(m),
8686
8727
  });
8728
+ // A3: the real `create` launcher (§4.3 steps 2-3): one document and one run per proposal identity.
8729
+ if (realCreate) {
8730
+ const bindings = runtime.docBindings;
8731
+ proposalLaunchers.register('create', new CreateLauncher({
8732
+ bindings,
8733
+ runExists: async (runId) => (await adapter.sessions()).includes(runId),
8734
+ docsRoot: (projectId) => projectDocsRoot(adapter, projectSettings, projectId),
8735
+ createDoc: (projectId, body, actorId) => createDocInProcess(adapter, {
8736
+ settings: projectSettings,
8737
+ pool: interactiveBridges,
8738
+ ...(runtime.docGrounding !== undefined ? { grounding: runtime.docGrounding } : {}),
8739
+ roster: () => rosterWithStanding(),
8740
+ proposalGrant,
8741
+ log: (m) => app.log.warn(m),
8742
+ }, projectId, body, actorId),
8743
+ launchBound: runtime.launchBoundDraft,
8744
+ ...(runtime.fenceBoundDraft !== undefined ? { fence: runtime.fenceBoundDraft } : {}),
8745
+ records: (chatId) => chatHistory?.messages(chatId) ?? chatTranscripts?.read(chatId) ?? [],
8746
+ instanceOf: (chatId) => chatHistory?.stateOf(chatId)?.instance ?? null,
8747
+ chatIdOf: (chatId, instance) => (chatHistory === null ? chatId : chatHistory.idOfInstance(chatId, instance)),
8748
+ repos: async () => (await adapter.listRepos()).map((r) => ({ id: r.id, name: typeof r.name === 'string' && r.name.trim() !== '' ? r.name.trim() : basename(r.root_path), rootPath: r.root_path })),
8749
+ graphFor: async (projectId, chatId) => {
8750
+ if (projectId === null)
8751
+ return chatScopes.engineOf(chatId)?.codeGraphDb ?? null;
8752
+ const decision = await resolveProjectGraphBinding(adapter, projectId, undefined).catch(() => ({ binding: null }));
8753
+ return decision.binding?.dbPath ?? null;
8754
+ },
8755
+ clisJsonFor: (seats) => {
8756
+ const roster = rosterWithStanding();
8757
+ const picked = seats.map((k) => roster.find((s) => s.key === k));
8758
+ return picked.some((s) => s === undefined) ? null : JSON.stringify(picked);
8759
+ },
8760
+ styleOf: (entry) => {
8761
+ const seed = CATALOG_SEEDS.find((c) => c.id === entry);
8762
+ return seed !== undefined && seed.launch.kind === 'document' ? seed.launch.style : 'web';
8763
+ },
8764
+ packetDeps: {
8765
+ runs: chatContextDeps.runs,
8766
+ brief: (view) => buildRunBrief(view, { workOutput: chatContextDeps.workOutput, ...(chatContextDeps.acceptanceLine !== undefined ? { acceptanceLine: chatContextDeps.acceptanceLine } : {}) }),
8767
+ docsRoot: chatContextDeps.docsRoot,
8768
+ attachment: (chatId, id) => {
8769
+ const hit = chatAttachments.resolve(chatId, [id])[0];
8770
+ return hit === undefined ? null : { name: hit.name, path: hit.path };
8771
+ },
8772
+ // The exact brief a turn was given, from the live chat's scratch root, beneath it (no link followed), only when
8773
+ // its bytes still hash to what the turn recorded (A1's binding). Gone after the chat ends or the daemon restarts.
8774
+ contextBrief: (chatId, relPath, sha) => {
8775
+ const cwd = chatScopes.get(chatId)?.cwd;
8776
+ if (cwd === undefined)
8777
+ return null;
8778
+ try {
8779
+ const data = readBeneath(realpathSync(cwd), relPath, BRIEF_COPY_MAX).data;
8780
+ return sha256Hex(data) === sha ? data : null;
8781
+ }
8782
+ catch {
8783
+ return null;
8784
+ }
8785
+ },
8786
+ },
8787
+ log: (m) => app.log.warn(m),
8788
+ }));
8789
+ }
8687
8790
  // ── Governed doc delete (crew#338) — the one door that changes BOTH stores ──
8688
8791
  // `DELETE /projects/:id/interactive/docs/:doc` retires the doc on the bridge AND drops crew's
8689
8792
  // handoff-ledger rows for it (the draft leg keys by document id, so a stale row claims the
@@ -8698,6 +8801,13 @@ runtime = {}) {
8698
8801
  // the operator's real ~/.wicked-crew ledgers. The real sweep always arrives from
8699
8802
  // `createServer`.
8700
8803
  dropDocLedgerRows: runtime.dropDocLedgerRows ?? (() => ({ ok: true, removed_keys: [] })),
8804
+ // A3: a document a chat proposal is still drafting (or landing) is not retired under it.
8805
+ busyReason: (projectId, doc) => {
8806
+ const pending = runtime.docBindings?.busy(`${projectId === 'default' ? UNFILED_MOUNT : projectId}/${doc}`) ?? null;
8807
+ return pending !== null && (pending.launchingAt !== undefined || pending.launchedAt !== undefined || pending.emittedAt !== undefined)
8808
+ ? `a chat proposal is still drafting this document (run ${pending.runId.slice(0, 8)}); wait for it to land, or stop that run first`
8809
+ : null;
8810
+ },
8701
8811
  log: (m) => app.log.warn(m),
8702
8812
  });
8703
8813
  // ── A demo run's video as an editable document (the video editor re-cuts it through E1's per-doc API) ──
@@ -8776,6 +8886,6 @@ runtime = {}) {
8776
8886
  const runEvalsPrepare = async (input) => {
8777
8887
  await prepareRunEvalsLaunch(input, () => runAndRecord(defaultEvalDeps, { project: null, baseline: false, id: randomUUID(), now: new Date() }), runtime.evalRunsDir);
8778
8888
  };
8779
- return { decideGate, decisions, chatRecorder, considerations, search, decorateRun, runEvalsPrepare, chatProposals };
8889
+ return { decideGate, decisions, chatRecorder, considerations, search, decorateRun, runEvalsPrepare, chatProposals, proposalLaunchers };
8780
8890
  }
8781
8891
  //# sourceMappingURL=routes.js.map