@svenroth-ai/shipwright 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/dist/assets/{EmbeddedTerminal-D3wpPNZP.js → EmbeddedTerminal-CqCQUDtp.js} +1 -1
- package/client/dist/assets/{abnfDiagram-VRR7QNED-BkLIltB3.js → abnfDiagram-VRR7QNED-DX2objTP.js} +1 -1
- package/client/dist/assets/{arc-Sw-Gh0nI.js → arc-DQmjbsFU.js} +1 -1
- package/client/dist/assets/{architectureDiagram-ZJ3FMSHR-rdxUi9Ks.js → architectureDiagram-ZJ3FMSHR-DAvCsPCW.js} +1 -1
- package/client/dist/assets/{blockDiagram-677ZJIJ3-Ch_9dj8a.js → blockDiagram-677ZJIJ3-DC-dlyYI.js} +1 -1
- package/client/dist/assets/{c4Diagram-LMCZKHZV-CkFlO65x.js → c4Diagram-LMCZKHZV-DSlmXfhE.js} +1 -1
- package/client/dist/assets/channel-BfmIcrp7.js +1 -0
- package/client/dist/assets/{chunk-2Q5K7J3B-CypX83xu.js → chunk-2Q5K7J3B-C111EHL6.js} +1 -1
- package/client/dist/assets/{chunk-32BRIVSS-DxVLYGdl.js → chunk-32BRIVSS-DrCYs2XG.js} +1 -1
- package/client/dist/assets/{chunk-5VM5RSS4-Cxk1evhx.js → chunk-5VM5RSS4-DIejoaXS.js} +1 -1
- package/client/dist/assets/{chunk-EX3LRPZG-n9q5S4yJ.js → chunk-EX3LRPZG-CGWNeH2g.js} +1 -1
- package/client/dist/assets/{chunk-JWPE2WC7-jeL4xvir.js → chunk-JWPE2WC7-CCH0SRFY.js} +1 -1
- package/client/dist/assets/{chunk-MOJQB5TN-DDSHN0cj.js → chunk-MOJQB5TN-B3OpZGRv.js} +1 -1
- package/client/dist/assets/{chunk-RYQCIY6F-C0bo383g.js → chunk-RYQCIY6F-BKhdp_8v.js} +1 -1
- package/client/dist/assets/{chunk-V7JOEXUC-C0jDXXAU.js → chunk-V7JOEXUC-B6Bu8x1y.js} +1 -1
- package/client/dist/assets/{chunk-VR4S4FIN-h-Rr4Pn2.js → chunk-VR4S4FIN-OgFi2_iB.js} +1 -1
- package/client/dist/assets/{chunk-XXDRQBXY-BexSo0-s.js → chunk-XXDRQBXY-xyjuuhxl.js} +1 -1
- package/client/dist/assets/classDiagram-OUVF2IWQ-HfUQZ6Uc.js +1 -0
- package/client/dist/assets/classDiagram-v2-EOCWNBFH-HfUQZ6Uc.js +1 -0
- package/client/dist/assets/{cose-bilkent-JH36ORCC-BQXzaioj.js → cose-bilkent-JH36ORCC-BQIH3SCK.js} +1 -1
- package/client/dist/assets/{cynefin-VYW2F7L2-BJIofhfe.js → cynefin-VYW2F7L2-oyLAz8te.js} +1 -1
- package/client/dist/assets/{cynefinDiagram-TSTJHNR4-DmPqTM1T.js → cynefinDiagram-TSTJHNR4-DoLCsH0l.js} +1 -1
- package/client/dist/assets/{dagre-VKFMJZFB-1xljvw6X.js → dagre-VKFMJZFB-D7djx1_Y.js} +1 -1
- package/client/dist/assets/{diagram-FQU43EPY-iyL9muCG.js → diagram-FQU43EPY-DFuYE0Vf.js} +1 -1
- package/client/dist/assets/{diagram-G47NLZAW-DREp2Th6.js → diagram-G47NLZAW-CffCZVXk.js} +1 -1
- package/client/dist/assets/{diagram-NH7WQ7WH-h0X3Is2S.js → diagram-NH7WQ7WH-BhrHtUlt.js} +1 -1
- package/client/dist/assets/{diagram-OA4YK3LP-Bj8YZRFJ.js → diagram-OA4YK3LP-CwQPbOn0.js} +1 -1
- package/client/dist/assets/{diagram-WEI45ONY-BZOf805d.js → diagram-WEI45ONY-abtYnYqH.js} +1 -1
- package/client/dist/assets/{ebnfDiagram-CCIWWBDH-DO5JnXda.js → ebnfDiagram-CCIWWBDH-DfV7gT4m.js} +1 -1
- package/client/dist/assets/{erDiagram-Q63AITRT-DRgcqg1T.js → erDiagram-Q63AITRT-vtbCprgH.js} +1 -1
- package/client/dist/assets/{flowDiagram-23GEKE2U-C3J9QmrW.js → flowDiagram-23GEKE2U-CVCgcyDF.js} +1 -1
- package/client/dist/assets/{ganttDiagram-NO4QXBWP-BIEl3Tkm.js → ganttDiagram-NO4QXBWP-BclyOgMb.js} +1 -1
- package/client/dist/assets/{gitGraphDiagram-IHSO6WYX-BNF0tFWC.js → gitGraphDiagram-IHSO6WYX-BXT4rIKj.js} +1 -1
- package/client/dist/assets/index-CKU9dY18.css +1 -0
- package/client/dist/assets/{index-BnTOrFHb.js → index-DOMl6atu.js} +232 -214
- package/client/dist/assets/{infoDiagram-FWYZ7A6U-DEyFxiyY.js → infoDiagram-FWYZ7A6U-Dr4mN24J.js} +1 -1
- package/client/dist/assets/{ishikawaDiagram-FXEZZL3T-B_ehlg3I.js → ishikawaDiagram-FXEZZL3T-BBUFWq5C.js} +1 -1
- package/client/dist/assets/{journeyDiagram-5HDEW3XC-Dp00FyJS.js → journeyDiagram-5HDEW3XC-CmclipcQ.js} +1 -1
- package/client/dist/assets/{kanban-definition-HUTT4EX6-CTAf16q8.js → kanban-definition-HUTT4EX6-eIzUeZ03.js} +1 -1
- package/client/dist/assets/{linear-9vNUgmYd.js → linear-DGiT7uKK.js} +1 -1
- package/client/dist/assets/{mermaid.core-A6tjaEVD.js → mermaid.core-BcLNGbxW.js} +4 -4
- package/client/dist/assets/{mindmap-definition-LN4V7U3C-DRW3bmCY.js → mindmap-definition-LN4V7U3C-C4ANnNiP.js} +1 -1
- package/client/dist/assets/{pegDiagram-2B236MQR-DJFCjHp2.js → pegDiagram-2B236MQR-DnMeh3Yh.js} +1 -1
- package/client/dist/assets/{pieDiagram-ENE6RG2P-KRd-sGvC.js → pieDiagram-ENE6RG2P-CKV_rkhF.js} +1 -1
- package/client/dist/assets/{quadrantDiagram-ABIIQ3AL-9TLZE3Hu.js → quadrantDiagram-ABIIQ3AL-BuUsGwHh.js} +1 -1
- package/client/dist/assets/{railroadDiagram-RFXS5EU6-y4tLtjXg.js → railroadDiagram-RFXS5EU6-BwwdKnFL.js} +1 -1
- package/client/dist/assets/{requirementDiagram-TGXJPOKE-B9EOQDQM.js → requirementDiagram-TGXJPOKE-CpQZouJV.js} +1 -1
- package/client/dist/assets/{sankeyDiagram-HTMAVEWB-zfwiDzkT.js → sankeyDiagram-HTMAVEWB-4fnDUNVa.js} +1 -1
- package/client/dist/assets/{sequenceDiagram-DBY2YBRQ-DW1LWepL.js → sequenceDiagram-DBY2YBRQ-BOcaWlM0.js} +1 -1
- package/client/dist/assets/{sizeCapture-X5ZJPWSS-BM81Mik0.js → sizeCapture-X5ZJPWSS-_pv-9R0c.js} +1 -1
- package/client/dist/assets/{stateDiagram-2N3HPSRC-D0EMIw1w.js → stateDiagram-2N3HPSRC-CFwCOonY.js} +1 -1
- package/client/dist/assets/stateDiagram-v2-6OUMAXLB-srkiu3Ib.js +1 -0
- package/client/dist/assets/{swimlanes-5IMT3BWC-CKXNCX6M.js → swimlanes-5IMT3BWC-DFa2tzxu.js} +2 -2
- package/client/dist/assets/swimlanesDiagram-G3AALYLV-Dq-0K9Zm.js +8 -0
- package/client/dist/assets/{timeline-definition-FHXFAJF6-ByzIfvL5.js → timeline-definition-FHXFAJF6-rhGYElgn.js} +1 -1
- package/client/dist/assets/{vennDiagram-L72KCM5P-Xep38xBH.js → vennDiagram-L72KCM5P-BoGjRxyA.js} +1 -1
- package/client/dist/assets/{wardleyDiagram-EHGQE667-DKWDpx6g.js → wardleyDiagram-EHGQE667-D-IGv5bH.js} +1 -1
- package/client/dist/assets/{xychartDiagram-FW5EYKEG-BGcwZTnu.js → xychartDiagram-FW5EYKEG-D9PFTCfm.js} +1 -1
- package/client/dist/index.html +2 -2
- package/lib/probes.mjs +10 -4
- package/lib/server.mjs +149 -12
- package/package.json +1 -1
- package/server/dist/core/sdk-sessions-merge.d.ts +19 -0
- package/server/dist/core/sdk-sessions-merge.d.ts.map +1 -1
- package/server/dist/core/sdk-sessions-merge.js +19 -5
- package/server/dist/core/sdk-sessions-merge.js.map +1 -1
- package/server/dist/core/sdk-sessions-refresh.d.ts +50 -0
- package/server/dist/core/sdk-sessions-refresh.d.ts.map +1 -0
- package/server/dist/core/sdk-sessions-refresh.js +69 -0
- package/server/dist/core/sdk-sessions-refresh.js.map +1 -0
- package/server/dist/core/sdk-sessions-store.d.ts +2 -0
- package/server/dist/core/sdk-sessions-store.d.ts.map +1 -1
- package/server/dist/core/sdk-sessions-store.js +7 -9
- package/server/dist/core/sdk-sessions-store.js.map +1 -1
- package/server/dist/core/shipwright-plugin-version-reader.d.ts +15 -0
- package/server/dist/core/shipwright-plugin-version-reader.d.ts.map +1 -0
- package/server/dist/core/shipwright-plugin-version-reader.js +49 -0
- package/server/dist/core/shipwright-plugin-version-reader.js.map +1 -0
- package/server/dist/external/inbox/_derive.d.ts +2 -25
- package/server/dist/external/inbox/_derive.d.ts.map +1 -1
- package/server/dist/external/inbox/_derive.js.map +1 -1
- package/server/dist/external/inbox/_lead.d.ts +23 -0
- package/server/dist/external/inbox/_lead.d.ts.map +1 -0
- package/server/dist/external/inbox/_lead.js +83 -0
- package/server/dist/external/inbox/_lead.js.map +1 -0
- package/server/dist/external/inbox/_types.d.ts +32 -0
- package/server/dist/external/inbox/_types.d.ts.map +1 -0
- package/server/dist/external/inbox/_types.js +10 -0
- package/server/dist/external/inbox/_types.js.map +1 -0
- package/server/dist/external/inbox/routes.d.ts.map +1 -1
- package/server/dist/external/inbox/routes.js +29 -1
- package/server/dist/external/inbox/routes.js.map +1 -1
- package/server/dist/external/launch/claim-holder-gate.d.ts +42 -0
- package/server/dist/external/launch/claim-holder-gate.d.ts.map +1 -0
- package/server/dist/external/launch/claim-holder-gate.js +87 -0
- package/server/dist/external/launch/claim-holder-gate.js.map +1 -0
- package/server/dist/external/launch/routes.d.ts.map +1 -1
- package/server/dist/external/launch/routes.js +23 -19
- package/server/dist/external/launch/routes.js.map +1 -1
- package/server/dist/external/org/lead-question-threads.d.ts +46 -0
- package/server/dist/external/org/lead-question-threads.d.ts.map +1 -0
- package/server/dist/external/org/lead-question-threads.js +132 -0
- package/server/dist/external/org/lead-question-threads.js.map +1 -0
- package/server/dist/external/org/mount.d.ts +3 -0
- package/server/dist/external/org/mount.d.ts.map +1 -1
- package/server/dist/external/org/mount.js +2 -1
- package/server/dist/external/org/mount.js.map +1 -1
- package/server/dist/external/org/usage.d.ts.map +1 -1
- package/server/dist/external/org/usage.js +20 -2
- package/server/dist/external/org/usage.js.map +1 -1
- package/server/dist/external/routes.d.ts.map +1 -1
- package/server/dist/external/routes.js +1 -7
- package/server/dist/external/routes.js.map +1 -1
- package/server/dist/routes/diagnostics.d.ts +12 -0
- package/server/dist/routes/diagnostics.d.ts.map +1 -1
- package/server/dist/routes/diagnostics.js +4 -1
- package/server/dist/routes/diagnostics.js.map +1 -1
- package/server/dist/routes/org-threads-composite.d.ts +24 -0
- package/server/dist/routes/org-threads-composite.d.ts.map +1 -0
- package/server/dist/routes/org-threads-composite.js +51 -0
- package/server/dist/routes/org-threads-composite.js.map +1 -0
- package/server/dist/routes/org.d.ts +5 -0
- package/server/dist/routes/org.d.ts.map +1 -1
- package/server/dist/routes/org.js +13 -0
- package/server/dist/routes/org.js.map +1 -1
- package/server/dist/terminal/pty-manager.d.ts +23 -1
- package/server/dist/terminal/pty-manager.d.ts.map +1 -1
- package/server/dist/terminal/pty-manager.js +22 -3
- package/server/dist/terminal/pty-manager.js.map +1 -1
- package/server/dist/terminal/pty-write-chunker.d.ts +109 -0
- package/server/dist/terminal/pty-write-chunker.d.ts.map +1 -0
- package/server/dist/terminal/pty-write-chunker.js +234 -0
- package/server/dist/terminal/pty-write-chunker.js.map +1 -0
- package/server/dist/types/org.d.ts +19 -0
- package/server/dist/types/org.d.ts.map +1 -1
- package/server/package.json +1 -1
- package/client/dist/assets/channel-BYPbLjRl.js +0 -1
- package/client/dist/assets/classDiagram-OUVF2IWQ-CsFIeTju.js +0 -1
- package/client/dist/assets/classDiagram-v2-EOCWNBFH-CsFIeTju.js +0 -1
- package/client/dist/assets/index-CYPc6qxw.css +0 -1
- package/client/dist/assets/stateDiagram-v2-6OUMAXLB-B3E4_xxT.js +0 -1
- package/client/dist/assets/swimlanesDiagram-G3AALYLV-BegQBqnC.js +0 -8
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type AggregatedEntry = {
|
|
2
|
+
kind: "ask_tool";
|
|
3
|
+
taskId: string;
|
|
4
|
+
sessionUuid: string;
|
|
5
|
+
taskTitle: string;
|
|
6
|
+
toolUseId: string;
|
|
7
|
+
toolName: string;
|
|
8
|
+
input: unknown;
|
|
9
|
+
bestEffort: true;
|
|
10
|
+
} | {
|
|
11
|
+
kind: "text_question";
|
|
12
|
+
taskId: string;
|
|
13
|
+
sessionUuid: string;
|
|
14
|
+
taskTitle: string;
|
|
15
|
+
questionId: string;
|
|
16
|
+
questionText: string;
|
|
17
|
+
bestEffort: true;
|
|
18
|
+
} | {
|
|
19
|
+
kind: "terminal_prompt";
|
|
20
|
+
taskId: string;
|
|
21
|
+
sessionUuid: string;
|
|
22
|
+
taskTitle: string;
|
|
23
|
+
promptText: string;
|
|
24
|
+
bestEffort: true;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "lead_question";
|
|
27
|
+
taskId: string;
|
|
28
|
+
sessionUuid: string;
|
|
29
|
+
taskTitle: string;
|
|
30
|
+
questionText: string;
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=_types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_types.d.ts","sourceRoot":"","sources":["../../../src/external/inbox/_types.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,eAAe,GACvB;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,IAAI,CAAC;CAClB,GACD;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,IAAI,CAAC;CAClB,GACD;IACE,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;CAClB,GACD;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* external/inbox/_types.ts — the aggregator's output shape, shared by
|
|
3
|
+
* `_derive.ts` (JSONL/terminal-derived kinds) and `_lead.ts` (the written
|
|
4
|
+
* `lead_question` kind). Split out to keep `_derive.ts` under the 300-LOC
|
|
5
|
+
* guideline. Discriminated union precedence: ask_tool > terminal_prompt >
|
|
6
|
+
* text_question; `lead_question` is independent of that precedence — it
|
|
7
|
+
* reflects a different waiting state on the same task, not a JSONL read.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=_types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_types.js","sourceRoot":"","sources":["../../../src/external/inbox/_types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/external/inbox/routes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/external/inbox/routes.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAUpE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,gBAAgB,CAAC;IACxB,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE;QACV,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAC7B,gBAAgB,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;KAClD,CAAC;CACH;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAoE7D"}
|
|
@@ -7,10 +7,13 @@
|
|
|
7
7
|
* `detectAwaitingUserQuestion`.
|
|
8
8
|
* - `terminal_prompt` — a waiting picker visible in the LIVE headless
|
|
9
9
|
* mirror but not (yet) journaled to the JSONL.
|
|
10
|
+
* - `lead_question` — a leadwright follow-up WRITTEN onto the task's
|
|
11
|
+
* `description` (FR-04.19), not derived from a JSONL/terminal read
|
|
12
|
+
* — see `./_lead.ts`.
|
|
10
13
|
*
|
|
11
14
|
* Precedence: ask_tool > terminal_prompt > text_question. terminal_prompt
|
|
12
15
|
* is a post-pass over the JSONL-derived set so the mtime-keyed cache
|
|
13
|
-
* stays byte-identical.
|
|
16
|
+
* stays byte-identical. `lead_question` is independent of that precedence.
|
|
14
17
|
*
|
|
15
18
|
* Phase A4 hot-path: per-session derive cache + negative-result cache
|
|
16
19
|
* — see `./_cache.ts`. JSONL cold-path + terminal_prompt post-pass live
|
|
@@ -19,6 +22,7 @@
|
|
|
19
22
|
import { Hono } from "hono";
|
|
20
23
|
import { inboxDeriveCache } from "./_cache.js";
|
|
21
24
|
import { deriveInboxFromJsonl, appendTerminalPrompts } from "./_derive.js";
|
|
25
|
+
import { appendLeadQuestions, extractLeadQuestionBody, LEAD_QUESTION_DISMISS_PREFIX, } from "./_lead.js";
|
|
22
26
|
export function createInboxRouter(deps) {
|
|
23
27
|
const app = new Hono();
|
|
24
28
|
const { store, watcher, ptyManager } = deps;
|
|
@@ -28,6 +32,7 @@ export function createInboxRouter(deps) {
|
|
|
28
32
|
watcher,
|
|
29
33
|
});
|
|
30
34
|
appendTerminalPrompts(entries, { store, ptyManager });
|
|
35
|
+
appendLeadQuestions(entries, { store });
|
|
31
36
|
if (storeDirty)
|
|
32
37
|
await store.persist();
|
|
33
38
|
return c.json({ items: entries });
|
|
@@ -52,6 +57,29 @@ export function createInboxRouter(deps) {
|
|
|
52
57
|
await store.persist();
|
|
53
58
|
return c.json({ ok: true, taskId: task.taskId });
|
|
54
59
|
}
|
|
60
|
+
// lead_question dismiss — the id is `lq-<taskId>`, not a JSONL toolUseId
|
|
61
|
+
// (a written kind has no toolUseId at all). Reuses `dismissedToolUseIds`
|
|
62
|
+
// as a generic dismissed-id set rather than adding a parallel field.
|
|
63
|
+
if (toolUseId.startsWith(LEAD_QUESTION_DISMISS_PREFIX)) {
|
|
64
|
+
const taskId = toolUseId.slice(LEAD_QUESTION_DISMISS_PREFIX.length);
|
|
65
|
+
const task = store.get(taskId);
|
|
66
|
+
if (task &&
|
|
67
|
+
extractLeadQuestionBody(task.description) !== null &&
|
|
68
|
+
!task.inbox.dismissedToolUseIds.includes(toolUseId)) {
|
|
69
|
+
store.patch(task.taskId, {
|
|
70
|
+
inbox: {
|
|
71
|
+
pendingToolUseIds: task.inbox.pendingToolUseIds,
|
|
72
|
+
dismissedToolUseIds: [
|
|
73
|
+
...task.inbox.dismissedToolUseIds,
|
|
74
|
+
toolUseId,
|
|
75
|
+
],
|
|
76
|
+
lastProcessedByteOffset: task.inbox.lastProcessedByteOffset,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
await store.persist();
|
|
80
|
+
return c.json({ ok: true, taskId: task.taskId });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
55
83
|
return c.json({ ok: false, error: "toolUseId not found in any pending set" }, 404);
|
|
56
84
|
});
|
|
57
85
|
return app;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/external/inbox/routes.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/external/inbox/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ExternalTask } from "../../core/sdk-sessions-store.js";
|
|
2
|
+
/**
|
|
3
|
+
* How long a claim stays launch-eligible after `claimedAt`. §10.9 left the
|
|
4
|
+
* VALUE unfixed ("a named limit"), justified only against one criterion: a
|
|
5
|
+
* holder who restarts LATE must still succeed, never be locked out (AC
|
|
6
|
+
* FR-04.22) — the fix for a stale token is re-claiming, not an indefinite
|
|
7
|
+
* grant. 24h is generous enough to survive a sleeping laptop, a slow beat,
|
|
8
|
+
* or an overnight gap between claim and restart, while still bounding how
|
|
9
|
+
* long an abandoned claim stays replayable.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CLAIM_LAUNCH_WINDOW_MS: number;
|
|
12
|
+
/**
|
|
13
|
+
* Doubt-review finding (iterate-2026-09-03-claim-holder-launch): `withinWindow`
|
|
14
|
+
* originally only bounded the claim's age from ABOVE, so a well-formed but
|
|
15
|
+
* FUTURE-dated `claimedAt` (clock skew between the webui host and the daemon
|
|
16
|
+
* host, or a daemon bug) made `Date.now() - claimedAtMs` negative — always
|
|
17
|
+
* `<= CLAIM_LAUNCH_WINDOW_MS`, reading as "very fresh" instead of being
|
|
18
|
+
* rejected. `sdk-sessions-validate.ts` does no date-sanity check on
|
|
19
|
+
* `claimedAt`, so this module is the only place that can catch it. A small
|
|
20
|
+
* tolerance (rather than rejecting any future timestamp outright) survives
|
|
21
|
+
* ordinary clock drift between two processes' wall clocks without reopening
|
|
22
|
+
* the gap: anything beyond it is refused as not-within-window, same as an
|
|
23
|
+
* expired claim.
|
|
24
|
+
*/
|
|
25
|
+
export declare const CLAIM_CLOCK_SKEW_TOLERANCE_MS: number;
|
|
26
|
+
export type ClaimGateResult = {
|
|
27
|
+
allowed: true;
|
|
28
|
+
} | {
|
|
29
|
+
allowed: false;
|
|
30
|
+
error: Record<string, unknown>;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Decide whether a launch request may proceed against a (possibly) claimed
|
|
34
|
+
* task. Returns `{ allowed: true }` when there is no active claim, or when
|
|
35
|
+
* `bodyClaimToken` matches the task's current `claimToken` AND the claim is
|
|
36
|
+
* within the launch window. Otherwise refuses with the `task_claimed`
|
|
37
|
+
* envelope — `claimExpired: true` distinguishes "right token, too old" from
|
|
38
|
+
* a foreign/absent token (trap: an expired claim must read differently from
|
|
39
|
+
* someone else's).
|
|
40
|
+
*/
|
|
41
|
+
export declare function checkClaimHolderGate(task: ExternalTask, bodyClaimToken: string | undefined): ClaimGateResult;
|
|
42
|
+
//# sourceMappingURL=claim-holder-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-holder-gate.d.ts","sourceRoot":"","sources":["../../../src/external/launch/claim-holder-gate.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAErE;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,6BAA6B,QAAgB,CAAC;AAE3D,MAAM,MAAM,eAAe,GACvB;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC;AAEvD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,YAAY,EAClB,cAAc,EAAE,MAAM,GAAG,SAAS,GACjC,eAAe,CAqCjB"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* external/launch/claim-holder-gate.ts — FR-04.22 / V5 (leadwright
|
|
3
|
+
* spec/lead-model-spec.md §5.2 + §10.9, PO decision 2026-08-16).
|
|
4
|
+
*
|
|
5
|
+
* The launch route refuses everyone while `claimToken` is set — EXCEPT the
|
|
6
|
+
* claim holder, who must be able to (re)launch (AC FR-04.22). `task` passed
|
|
7
|
+
* in here MUST already be the freshly-disk-read row (see
|
|
8
|
+
* SdkSessionsStore.refreshRowFromDisk in ../../core/sdk-sessions-store.ts) —
|
|
9
|
+
* this module makes no disk access itself and trusts the caller's freshness.
|
|
10
|
+
*
|
|
11
|
+
* §10.9 decided variant (a) plus a time window: the exception accepts the
|
|
12
|
+
* caller presenting the CURRENT claimToken, and additionally requires
|
|
13
|
+
* `claimedAt` to be no older than CLAIM_LAUNCH_WINDOW_MS. Explicitly
|
|
14
|
+
* rejected: making the token single-use (valid only while `launchedAt` is
|
|
15
|
+
* unset) — that breaks AC FR-04.22's "a restart by the holder succeeds".
|
|
16
|
+
*
|
|
17
|
+
* Named residual risk (§8, §10.9): both sides run as the same local user
|
|
18
|
+
* with sdk-sessions.json readable, so this defends against a MISTAKE (a
|
|
19
|
+
* stale browser tab racing a daemon claim), not a local attacker — a
|
|
20
|
+
* process on the same machine under the same user can read the token
|
|
21
|
+
* directly. The window only shrinks that exposure; it does not close it.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* How long a claim stays launch-eligible after `claimedAt`. §10.9 left the
|
|
25
|
+
* VALUE unfixed ("a named limit"), justified only against one criterion: a
|
|
26
|
+
* holder who restarts LATE must still succeed, never be locked out (AC
|
|
27
|
+
* FR-04.22) — the fix for a stale token is re-claiming, not an indefinite
|
|
28
|
+
* grant. 24h is generous enough to survive a sleeping laptop, a slow beat,
|
|
29
|
+
* or an overnight gap between claim and restart, while still bounding how
|
|
30
|
+
* long an abandoned claim stays replayable.
|
|
31
|
+
*/
|
|
32
|
+
export const CLAIM_LAUNCH_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
33
|
+
/**
|
|
34
|
+
* Doubt-review finding (iterate-2026-09-03-claim-holder-launch): `withinWindow`
|
|
35
|
+
* originally only bounded the claim's age from ABOVE, so a well-formed but
|
|
36
|
+
* FUTURE-dated `claimedAt` (clock skew between the webui host and the daemon
|
|
37
|
+
* host, or a daemon bug) made `Date.now() - claimedAtMs` negative — always
|
|
38
|
+
* `<= CLAIM_LAUNCH_WINDOW_MS`, reading as "very fresh" instead of being
|
|
39
|
+
* rejected. `sdk-sessions-validate.ts` does no date-sanity check on
|
|
40
|
+
* `claimedAt`, so this module is the only place that can catch it. A small
|
|
41
|
+
* tolerance (rather than rejecting any future timestamp outright) survives
|
|
42
|
+
* ordinary clock drift between two processes' wall clocks without reopening
|
|
43
|
+
* the gap: anything beyond it is refused as not-within-window, same as an
|
|
44
|
+
* expired claim.
|
|
45
|
+
*/
|
|
46
|
+
export const CLAIM_CLOCK_SKEW_TOLERANCE_MS = 5 * 60 * 1000;
|
|
47
|
+
/**
|
|
48
|
+
* Decide whether a launch request may proceed against a (possibly) claimed
|
|
49
|
+
* task. Returns `{ allowed: true }` when there is no active claim, or when
|
|
50
|
+
* `bodyClaimToken` matches the task's current `claimToken` AND the claim is
|
|
51
|
+
* within the launch window. Otherwise refuses with the `task_claimed`
|
|
52
|
+
* envelope — `claimExpired: true` distinguishes "right token, too old" from
|
|
53
|
+
* a foreign/absent token (trap: an expired claim must read differently from
|
|
54
|
+
* someone else's).
|
|
55
|
+
*/
|
|
56
|
+
export function checkClaimHolderGate(task, bodyClaimToken) {
|
|
57
|
+
if (typeof task.claimToken !== "string" || task.claimToken.length === 0) {
|
|
58
|
+
return { allowed: true };
|
|
59
|
+
}
|
|
60
|
+
const tokenMatches = bodyClaimToken !== undefined && bodyClaimToken === task.claimToken;
|
|
61
|
+
const claimedAtMs = task.claimedAt ? Date.parse(task.claimedAt) : NaN;
|
|
62
|
+
const age = Date.now() - claimedAtMs;
|
|
63
|
+
const withinWindow = Number.isFinite(claimedAtMs) &&
|
|
64
|
+
age >= -CLAIM_CLOCK_SKEW_TOLERANCE_MS &&
|
|
65
|
+
age <= CLAIM_LAUNCH_WINDOW_MS;
|
|
66
|
+
if (tokenMatches && withinWindow) {
|
|
67
|
+
return { allowed: true };
|
|
68
|
+
}
|
|
69
|
+
console.warn(JSON.stringify({
|
|
70
|
+
level: "warn",
|
|
71
|
+
message: "task_claimed: launch refused while claimToken is set",
|
|
72
|
+
taskId: task.taskId,
|
|
73
|
+
claimedBy: task.claimedBy,
|
|
74
|
+
claimedAt: task.claimedAt,
|
|
75
|
+
claimPid: task.claimPid,
|
|
76
|
+
reason: tokenMatches ? "claim_expired" : "foreign_claim",
|
|
77
|
+
}));
|
|
78
|
+
const error = {
|
|
79
|
+
error: "task_claimed",
|
|
80
|
+
claimedBy: task.claimedBy,
|
|
81
|
+
claimedAt: task.claimedAt,
|
|
82
|
+
};
|
|
83
|
+
if (tokenMatches)
|
|
84
|
+
error.claimExpired = true;
|
|
85
|
+
return { allowed: false, error };
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=claim-holder-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-holder-gate.js","sourceRoot":"","sources":["../../../src/external/launch/claim-holder-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAM3D;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAAkB,EAClB,cAAkC;IAElC,IAAI,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,MAAM,YAAY,GAChB,cAAc,KAAK,SAAS,IAAI,cAAc,KAAK,IAAI,CAAC,UAAU,CAAC;IACrE,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACtE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC;IACrC,MAAM,YAAY,GAChB,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC;QAC5B,GAAG,IAAI,CAAC,6BAA6B;QACrC,GAAG,IAAI,sBAAsB,CAAC;IAEhC,IAAI,YAAY,IAAI,YAAY,EAAE,CAAC;QACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,OAAO,CAAC,IAAI,CACV,IAAI,CAAC,SAAS,CAAC;QACb,KAAK,EAAE,MAAM;QACb,OAAO,EAAE,sDAAsD;QAC/D,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,eAAe;KACzD,CAAC,CACH,CAAC;IAEF,MAAM,KAAK,GAA4B;QACrC,KAAK,EAAE,cAAc;QACrB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC;IACF,IAAI,YAAY;QAAE,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC;IAC5C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACnC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/external/launch/routes.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EACL,gBAAgB,EAEjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/external/launch/routes.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EACL,gBAAgB,EAEjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AActE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,gBAAgB,CAAC;IACxB,UAAU,EAAE;QAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC7C,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,wBAAwB,GAAG,SAAS,CAAC;IACtE,eAAe,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACvE;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAwN/D"}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import { Hono } from "hono";
|
|
21
21
|
import { withLiveSession } from "../_shared/helpers.js";
|
|
22
22
|
import { parseLaunchBody, applyPhaseTaskBranch, applyCampaignBranch, applyCampaignStepBranch, applyMasterRunBranch, applyActionSubstitutionBranch, applyLegacyFallbackBranch, } from "./_helpers.js";
|
|
23
|
+
import { checkClaimHolderGate } from "./claim-holder-gate.js";
|
|
23
24
|
export function createLaunchRouter(deps) {
|
|
24
25
|
const app = new Hono();
|
|
25
26
|
const { store, ptyManager, getProjectById, runConfigReader, jsonlExistsOnDisk } = deps;
|
|
@@ -28,7 +29,7 @@ export function createLaunchRouter(deps) {
|
|
|
28
29
|
const body = rawBody && typeof rawBody === "object" && !Array.isArray(rawBody)
|
|
29
30
|
? rawBody
|
|
30
31
|
: {};
|
|
31
|
-
|
|
32
|
+
let task = store.get(c.req.param("id"));
|
|
32
33
|
if (!task)
|
|
33
34
|
return c.json({ error: "Task not found" }, 404);
|
|
34
35
|
// Section 03 (iterate 3) — O20 idempotency guard. Reject re-launch on
|
|
@@ -39,24 +40,27 @@ export function createLaunchRouter(deps) {
|
|
|
39
40
|
return c.json({ error: "launch_invalid_state", state: task.state }, 409);
|
|
40
41
|
}
|
|
41
42
|
// iterate-2026-05-14 lead-foundation-task-schema (leadwright Phase 1).
|
|
42
|
-
// While a task is claimed the
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
43
|
+
// While a task is claimed the launch route MUST step aside for anyone
|
|
44
|
+
// but the claim HOLDER (FR-04.22/V5, iterate-2026-09-03-claim-holder-launch)
|
|
45
|
+
// — so a stranger and the daemon don't fight over the same session
|
|
46
|
+
// (stale SQLite lock, interleaved JSONL, double-running shells), while
|
|
47
|
+
// the holder itself can still (re)launch. External review HIGH-2: only
|
|
48
|
+
// `claimToken` triggers the 409.
|
|
49
|
+
//
|
|
50
|
+
// The in-memory row can be STALE — `load()` runs once at server boot
|
|
51
|
+
// (index.ts), so a foreign claim written to disk by the leadwright
|
|
52
|
+
// daemon only reaches this process's memory via its OWN persist(). A
|
|
53
|
+
// webui that has persisted nothing since the claim was written would
|
|
54
|
+
// otherwise see no claim at all (the normal case for an idle daemon
|
|
55
|
+
// with no browser open). Refresh this one row from disk first — never
|
|
56
|
+
// re-run `load()` (a guarded no-op after the first call).
|
|
57
|
+
const freshTask = await store.refreshRowFromDisk(task.taskId);
|
|
58
|
+
if (freshTask)
|
|
59
|
+
task = freshTask;
|
|
60
|
+
const bodyClaimToken = typeof body.claimToken === "string" ? body.claimToken : undefined;
|
|
61
|
+
const claimGate = checkClaimHolderGate(task, bodyClaimToken);
|
|
62
|
+
if (!claimGate.allowed) {
|
|
63
|
+
return c.json(claimGate.error, 409);
|
|
60
64
|
}
|
|
61
65
|
// Parse + apply once-set-always-used (v0.4.1 fallback contract).
|
|
62
66
|
const parseResult = parseLaunchBody(body, task);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/external/launch/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAQ5B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,uBAAuB,EACvB,oBAAoB,EACpB,6BAA6B,EAC7B,yBAAyB,GAE1B,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/external/launch/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface LeadQuestionRoundMirror {
|
|
2
|
+
round: number;
|
|
3
|
+
questionType: string;
|
|
4
|
+
question: string;
|
|
5
|
+
askedAt: string;
|
|
6
|
+
answer?: {
|
|
7
|
+
text: string;
|
|
8
|
+
answeredAt: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export interface LeadQuestionThreadMirror {
|
|
12
|
+
taskId: string;
|
|
13
|
+
dedupKey: string;
|
|
14
|
+
rounds: LeadQuestionRoundMirror[];
|
|
15
|
+
}
|
|
16
|
+
interface LeadQuestionThreadsFileMirror {
|
|
17
|
+
version: number;
|
|
18
|
+
threads: Record<string, LeadQuestionThreadMirror>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Structural-only parse. `null` on anything that isn't a well-formed,
|
|
22
|
+
* KNOWN-version threads file — every such case reads as "no threads" to
|
|
23
|
+
* every caller (see file header).
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseLeadQuestionThreadsFile(raw: string): LeadQuestionThreadsFileMirror | null;
|
|
26
|
+
export interface LeadQuestionThreadsRouteDeps {
|
|
27
|
+
leadsRoot: string;
|
|
28
|
+
/** Injectable for tests; production wires the real lstatSync. */
|
|
29
|
+
lstatSync?: (p: string) => {
|
|
30
|
+
isSymbolicLink(): boolean;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export type LeadQuestionThreadsReadResult = {
|
|
34
|
+
ok: true;
|
|
35
|
+
threads: Record<string, LeadQuestionThreadMirror>;
|
|
36
|
+
} | {
|
|
37
|
+
ok: false;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Pure core. Always succeeds with a (possibly empty) threads map — a
|
|
41
|
+
* missing/symlinked/unreadable/invalid/unknown-version file degrades to
|
|
42
|
+
* `{ ok: false }`, which callers treat identically to "no threads yet".
|
|
43
|
+
*/
|
|
44
|
+
export declare function readLeadQuestionThreadsCore(deps: LeadQuestionThreadsRouteDeps, leadId: string): LeadQuestionThreadsReadResult;
|
|
45
|
+
export {};
|
|
46
|
+
//# sourceMappingURL=lead-question-threads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lead-question-threads.d.ts","sourceRoot":"","sources":["../../../src/external/org/lead-question-threads.ts"],"names":[],"mappings":"AA+BA,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,uBAAuB,EAAE,CAAC;CACnC;AAED,UAAU,6BAA6B;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;CACnD;AAkCD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,MAAM,GAAG,6BAA6B,GAAG,IAAI,CA4B9F;AAED,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK;QAAE,cAAc,IAAI,OAAO,CAAA;KAAE,CAAC;CAC1D;AAED,MAAM,MAAM,6BAA6B,GACrC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAA;CAAE,GAC/D;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC;AAElB;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,4BAA4B,EAClC,MAAM,EAAE,MAAM,GACb,6BAA6B,CAiC/B"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* external/org/lead-question-threads.ts — reads leadwright's
|
|
3
|
+
* `<leadsRoot>/<lead-id>/lead-question-threads.json` (FR-04.42, pinned by
|
|
4
|
+
* leadwright#35's `lib/lead-question-thread.ts`). This module is the
|
|
5
|
+
* webui-side half of that contract: leadwright owns the write path and the
|
|
6
|
+
* exposure shape (`exposeLeadQuestionThread`) that this mirrors; this repo
|
|
7
|
+
* only ever reads.
|
|
8
|
+
*
|
|
9
|
+
* Shapes below are a VERBATIM, INDEPENDENT mirror of leadwright's
|
|
10
|
+
* `LeadQuestionRound` / `LeadQuestionThread` / `LeadQuestionThreadsFile`
|
|
11
|
+
* (CLAUDE.md rule 7 — no cross-package import), not a re-implementation of
|
|
12
|
+
* its write-time invariants.
|
|
13
|
+
*
|
|
14
|
+
* Unlike every sibling reader in this directory (`usage.ts`, `last-run.ts`,
|
|
15
|
+
* `org-chart.ts`), a missing file, an unreadable file, invalid JSON, an
|
|
16
|
+
* unknown `version`, or a malformed shape all collapse to the SAME "no
|
|
17
|
+
* threads" result — never a distinct error status. A lead with no follow-up
|
|
18
|
+
* history is the overwhelming steady state (most leads have never asked a
|
|
19
|
+
* question), and the Org page must keep rendering cleanly either way (AC-d):
|
|
20
|
+
* there is no page-level error surface for this data the way there is for
|
|
21
|
+
* `org-chart.json` (`org_chart_invalid`, 502).
|
|
22
|
+
*/
|
|
23
|
+
import { readFileSync, lstatSync } from "node:fs";
|
|
24
|
+
import path from "node:path";
|
|
25
|
+
import { realPathGuard } from "../../core/path-guard.js";
|
|
26
|
+
import { LEAD_ID_RE } from "./_helpers.js";
|
|
27
|
+
const LEAD_QUESTION_THREADS_VERSION = 1;
|
|
28
|
+
function isValidAnswer(v) {
|
|
29
|
+
if (typeof v !== "object" || v === null)
|
|
30
|
+
return false;
|
|
31
|
+
const a = v;
|
|
32
|
+
return typeof a.text === "string" && typeof a.answeredAt === "string";
|
|
33
|
+
}
|
|
34
|
+
function isValidRound(v) {
|
|
35
|
+
if (typeof v !== "object" || v === null)
|
|
36
|
+
return false;
|
|
37
|
+
const r = v;
|
|
38
|
+
if (typeof r.round !== "number" ||
|
|
39
|
+
typeof r.questionType !== "string" ||
|
|
40
|
+
typeof r.question !== "string" ||
|
|
41
|
+
typeof r.askedAt !== "string") {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (r.answer !== undefined && !isValidAnswer(r.answer))
|
|
45
|
+
return false;
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
function isValidThread(v) {
|
|
49
|
+
if (typeof v !== "object" || v === null)
|
|
50
|
+
return false;
|
|
51
|
+
const t = v;
|
|
52
|
+
return (typeof t.taskId === "string" &&
|
|
53
|
+
typeof t.dedupKey === "string" &&
|
|
54
|
+
Array.isArray(t.rounds) &&
|
|
55
|
+
t.rounds.every(isValidRound));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Structural-only parse. `null` on anything that isn't a well-formed,
|
|
59
|
+
* KNOWN-version threads file — every such case reads as "no threads" to
|
|
60
|
+
* every caller (see file header).
|
|
61
|
+
*/
|
|
62
|
+
export function parseLeadQuestionThreadsFile(raw) {
|
|
63
|
+
let parsed;
|
|
64
|
+
try {
|
|
65
|
+
parsed = JSON.parse(raw);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
71
|
+
return null;
|
|
72
|
+
const obj = parsed;
|
|
73
|
+
if (obj.version !== LEAD_QUESTION_THREADS_VERSION) {
|
|
74
|
+
// Not a crash, not a page-level error (AC-d) — but a schema bump on
|
|
75
|
+
// leadwright's side would otherwise blank every Org-page thread with no
|
|
76
|
+
// operator-visible signal anywhere, so this one branch gets a breadcrumb.
|
|
77
|
+
console.warn(`lead-question-threads.json: unknown version ${JSON.stringify(obj.version)}, expected ${LEAD_QUESTION_THREADS_VERSION} — treating as no threads`);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
if (typeof obj.threads !== "object" || obj.threads === null || Array.isArray(obj.threads)) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const threadsIn = obj.threads;
|
|
84
|
+
const threads = {};
|
|
85
|
+
for (const [taskId, thread] of Object.entries(threadsIn)) {
|
|
86
|
+
if (!isValidThread(thread))
|
|
87
|
+
return null;
|
|
88
|
+
threads[taskId] = thread;
|
|
89
|
+
}
|
|
90
|
+
return { version: obj.version, threads };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Pure core. Always succeeds with a (possibly empty) threads map — a
|
|
94
|
+
* missing/symlinked/unreadable/invalid/unknown-version file degrades to
|
|
95
|
+
* `{ ok: false }`, which callers treat identically to "no threads yet".
|
|
96
|
+
*/
|
|
97
|
+
export function readLeadQuestionThreadsCore(deps, leadId) {
|
|
98
|
+
const { leadsRoot } = deps;
|
|
99
|
+
// Defense in depth, mirroring usage.ts/last-run.ts: callers pass a
|
|
100
|
+
// chart-derived leadId (org-chart.json's parser validates each lead's
|
|
101
|
+
// FIELDS but not that its object KEY is kebab-case), so this reader
|
|
102
|
+
// re-validates its own leadId regardless of caller rather than trusting
|
|
103
|
+
// the containment check below to be the only guard.
|
|
104
|
+
if (!LEAD_ID_RE.test(leadId))
|
|
105
|
+
return { ok: false };
|
|
106
|
+
const lstat = deps.lstatSync ?? ((p) => lstatSync(p));
|
|
107
|
+
const absolute = path.join(leadsRoot, leadId, "lead-question-threads.json");
|
|
108
|
+
let lst;
|
|
109
|
+
try {
|
|
110
|
+
lst = lstat(absolute);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return { ok: false };
|
|
114
|
+
}
|
|
115
|
+
if (lst.isSymbolicLink())
|
|
116
|
+
return { ok: false };
|
|
117
|
+
const containment = realPathGuard(leadsRoot, absolute);
|
|
118
|
+
if (!containment.ok)
|
|
119
|
+
return { ok: false };
|
|
120
|
+
let raw;
|
|
121
|
+
try {
|
|
122
|
+
raw = readFileSync(absolute, "utf8");
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return { ok: false };
|
|
126
|
+
}
|
|
127
|
+
const file = parseLeadQuestionThreadsFile(raw);
|
|
128
|
+
if (!file)
|
|
129
|
+
return { ok: false };
|
|
130
|
+
return { ok: true, threads: file.threads };
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=lead-question-threads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lead-question-threads.js","sourceRoot":"","sources":["../../../src/external/org/lead-question-threads.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { Hono } from "hono";
|
|
2
2
|
import { type OrgRouterDeps } from "./routes.js";
|
|
3
|
+
import { type OrgApiRouterDeps } from "../../routes/org.js";
|
|
3
4
|
export interface MountOrgRoutersDeps {
|
|
4
5
|
honoHost?: string;
|
|
5
6
|
leadsRoot?: string;
|
|
6
7
|
leadsRouteSecret?: string;
|
|
7
8
|
orgLstatSync?: OrgRouterDeps["lstatSync"];
|
|
8
9
|
orgWithDecisionsLock?: OrgRouterDeps["withDecisionsLock"];
|
|
10
|
+
/** FR-04.42 — task-title lookup for `GET /api/org/threads`. */
|
|
11
|
+
store?: OrgApiRouterDeps["store"];
|
|
9
12
|
}
|
|
10
13
|
/** FR-04.38 — mounted only when `honoHost` + `leadsRoot` are both provided. */
|
|
11
14
|
export declare function mountOrgRouters(app: Hono, deps: MountOrgRoutersDeps): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../../src/external/org/mount.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAEjC,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../../src/external/org/mount.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAEjC,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEhF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC1C,oBAAoB,CAAC,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC;IAC1D,+DAA+D;IAC/D,KAAK,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;CACnC;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAwB1E"}
|
|
@@ -14,7 +14,7 @@ import { createOrgRouter } from "./routes.js";
|
|
|
14
14
|
import { createOrgApiRouter } from "../../routes/org.js";
|
|
15
15
|
/** FR-04.38 — mounted only when `honoHost` + `leadsRoot` are both provided. */
|
|
16
16
|
export function mountOrgRouters(app, deps) {
|
|
17
|
-
const { honoHost, leadsRoot, leadsRouteSecret, orgLstatSync, orgWithDecisionsLock } = deps;
|
|
17
|
+
const { honoHost, leadsRoot, leadsRouteSecret, orgLstatSync, orgWithDecisionsLock, store } = deps;
|
|
18
18
|
if (!honoHost || !leadsRoot)
|
|
19
19
|
return;
|
|
20
20
|
app.route("/", createOrgRouter({
|
|
@@ -29,6 +29,7 @@ export function mountOrgRouters(app, deps) {
|
|
|
29
29
|
leadsRoot,
|
|
30
30
|
lstatSync: orgLstatSync,
|
|
31
31
|
withDecisionsLock: orgWithDecisionsLock,
|
|
32
|
+
store,
|
|
32
33
|
}));
|
|
33
34
|
}
|
|
34
35
|
//# sourceMappingURL=mount.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.js","sourceRoot":"","sources":["../../../src/external/org/mount.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAE,eAAe,EAAsB,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"mount.js","sourceRoot":"","sources":["../../../src/external/org/mount.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAE,eAAe,EAAsB,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAyB,MAAM,qBAAqB,CAAC;AAYhF,+EAA+E;AAC/E,MAAM,UAAU,eAAe,CAAC,GAAS,EAAE,IAAyB;IAClE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAClG,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS;QAAE,OAAO;IAEpC,GAAG,CAAC,KAAK,CACP,GAAG,EACH,eAAe,CAAC;QACd,QAAQ;QACR,SAAS;QACT,gBAAgB;QAChB,SAAS,EAAE,YAAY;QACvB,iBAAiB,EAAE,oBAAoB;KACxC,CAAC,CACH,CAAC;IACF,GAAG,CAAC,KAAK,CACP,GAAG,EACH,kBAAkB,CAAC;QACjB,QAAQ;QACR,SAAS;QACT,SAAS,EAAE,YAAY;QACvB,iBAAiB,EAAE,oBAAoB;QACvC,KAAK;KACN,CAAC,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../../src/external/org/usage.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAIjC,OAAO,EAAc,+BAA+B,EAAE,MAAM,eAAe,CAAC;AAE5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,+BAA+B,EAAE,CAAC;AAC3C,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../../src/external/org/usage.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAIjC,OAAO,EAAc,+BAA+B,EAAE,MAAM,eAAe,CAAC;AAE5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,+BAA+B,EAAE,CAAC;AAC3C,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AA0CxD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB;kFAC8E;IAC9E,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,cAAc,IAAI,OAAO,CAAA;KAAE,CAAC;CAC7D;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,aAAa,CAAA;CAAE,GACpC;IAAE,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAEjG,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe,CA4E/E;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CAKxE"}
|