@gitroll/tacitt-cli 0.6.10 → 0.6.12

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 (63) hide show
  1. package/README.md +47 -4
  2. package/bin/INDEX.md +6 -4
  3. package/bin/tacitt.cjs +1 -0
  4. package/dist/{agent-setup-LPd8WEsN.mjs → agent-setup-B12VbvN_.mjs} +2 -2
  5. package/dist/agent-setup-worker.mjs +5 -5
  6. package/dist/cli.mjs +39 -25
  7. package/dist/codex-title-worker.mjs +2 -2
  8. package/dist/configured-preview-worker.mjs +5 -5
  9. package/dist/configured-session-worker.mjs +27 -27
  10. package/dist/{frame-ByT1xayJ.mjs → frame-B3He21G8.mjs} +1 -1
  11. package/dist/{heap-CM298Yu0.mjs → heap-Blrat--4.mjs} +469 -30
  12. package/dist/inspection-worker.mjs +4 -4
  13. package/dist/{job-BHIwElNF.mjs → job-CBuIMTQ0.mjs} +57 -22
  14. package/dist/{locate-Cwv6WH8n.mjs → locate-D-ZaHpI0.mjs} +184 -18
  15. package/dist/{model-zvn5GUJ9.mjs → model-DR9odhWP.mjs} +1 -1
  16. package/dist/operation-executor.mjs +3 -3
  17. package/dist/preview-worker.mjs +1 -1
  18. package/dist/privacy-validator.mjs +49 -2
  19. package/dist/{query-By41gaKm.mjs → query-BYDcNKy2.mjs} +1869 -716
  20. package/dist/{run-ZQx7p4Rw.mjs → run-DQ9xyKSt.mjs} +4 -4
  21. package/dist/session-process.mjs +20 -0
  22. package/dist/{src-D_DfY--2.mjs → src-CKQlTA2Z.mjs} +297 -207
  23. package/dist/{src-3jvMvSQn.mjs → src-DeNJ8vOD.mjs} +535 -140
  24. package/dist/{src-BqtdFVq7.mjs → src-Do-qzuMn.mjs} +2329 -1786
  25. package/dist/store-database-worker.mjs +1 -1
  26. package/dist/web/assets/index-BgDdPL1c.js +172 -0
  27. package/dist/web/assets/index-DzOozqms.css +1 -0
  28. package/dist/web/index.html +3 -2
  29. package/package.json +2 -1
  30. package/skills/INDEX.md +6 -4
  31. package/skills/tacitt/INDEX.md +7 -6
  32. package/skills/tacitt/SKILL.md +11 -11
  33. package/skills/tacitt/references/INDEX.md +14 -11
  34. package/skills/tacitt/references/b-method.md +4 -4
  35. package/skills/tacitt/references/bootstrap.md +19 -5
  36. package/skills/tacitt/references/classification.md +4 -4
  37. package/skills/tacitt/references/commands.md +51 -34
  38. package/skills/tacitt/references/journey.md +7 -7
  39. package/skills/tacitt/references/parallel-review.md +35 -35
  40. package/skills/tacitt/references/send.md +15 -6
  41. package/skills/tacitt/scripts/INDEX.md +40 -37
  42. package/skills/tacitt/scripts/answer.mjs +91 -15
  43. package/skills/tacitt/scripts/journey-card-consolidation.mjs +1 -1
  44. package/skills/tacitt/scripts/journey-classification-metadata.mjs +75 -26
  45. package/skills/tacitt/scripts/journey-classification-progress.mjs +1 -2
  46. package/skills/tacitt/scripts/journey-classification-tasks.mjs +28 -8
  47. package/skills/tacitt/scripts/journey-classification.mjs +6 -1
  48. package/skills/tacitt/scripts/journey-consolidation.mjs +1 -0
  49. package/skills/tacitt/scripts/journey-panel-host.d.mts +2 -0
  50. package/skills/tacitt/scripts/journey-panel-host.mjs +25 -2
  51. package/skills/tacitt/scripts/journey-scan-timing.d.mts +5 -0
  52. package/skills/tacitt/scripts/journey-scan-timing.mjs +31 -4
  53. package/skills/tacitt/scripts/journey-state.d.mts +9 -1
  54. package/skills/tacitt/scripts/journey-state.mjs +95 -8
  55. package/skills/tacitt/scripts/journey-tasks.d.mts +1 -0
  56. package/skills/tacitt/scripts/journey-tasks.mjs +24 -9
  57. package/skills/tacitt/scripts/journey.d.mts +44 -31
  58. package/skills/tacitt/scripts/journey.mjs +34 -7
  59. package/skills/tacitt/scripts/privacy-validator.mjs +188 -11
  60. package/skills/tacitt/scripts/review.d.mts +7 -6
  61. package/skills/tacitt/scripts/review.mjs +922 -205
  62. package/dist/web/assets/index-B14e6_-z.js +0 -174
  63. package/dist/web/assets/index-CYJBq1px.css +0 -1
@@ -1,7 +1,7 @@
1
- import { Bn as nativeItems, En as walkNativeFields, Nn as parseNativeRecord, Rn as nativeBoolean, Vn as nativeText, Wn as retainedMainTextNodes, Xn as defaultLimits, an as observeSource, on as sourceSnapshotId, ur as ProcessingError, zn as nativeField } from "./locate-Cwv6WH8n.mjs";
2
- import { Mt as nativeAdapterVersions, it as reviewInventory, ui as gitMetadataBudgetBytes } from "./src-BqtdFVq7.mjs";
3
- import { a as isRecord, i as errorCode, n as checkDiscoveryAbort, o as unresolvedMetadata, r as discoveryBounds, t as DiscoveryWarnings } from "./model-zvn5GUJ9.mjs";
4
- import { c as previewLimits, d as checkOpenSource, f as checkSource, h as openSource, i as pageFits, l as previewSourceVersion, m as noSymlinks, p as fileSnapshot, r as previewHeapLimits, s as previewMessageSchema, u as createZstdFrameCheck } from "./run-ZQx7p4Rw.mjs";
1
+ import { Gn as nativeField, Kn as nativeItems, Mn as walkNativeFields, Wn as nativeBoolean, Xn as retainedMainTextNodes, _r as declaredVersion, br as processingSite, dn as observeSource, fn as sourceSnapshotId, gr as ProcessingError, nr as defaultLimits, qn as nativeText, zn as parseNativeRecord } from "./locate-D-ZaHpI0.mjs";
2
+ import { $r as nativeAdapterVersions, Oi as gitMetadataBudgetBytes, it as reviewInventory } from "./src-Do-qzuMn.mjs";
3
+ import { a as isRecord, i as errorCode, n as checkDiscoveryAbort, o as unresolvedMetadata, r as discoveryBounds, t as DiscoveryWarnings } from "./model-DR9odhWP.mjs";
4
+ import { c as previewLimits, d as checkOpenSource, f as checkSource, h as openSource, i as pageFits, l as previewSourceVersion, m as noSymlinks, p as fileSnapshot, r as previewHeapLimits, s as previewMessageSchema, u as createZstdFrameCheck } from "./run-DQ9xyKSt.mjs";
5
5
  import { a as titleHintLimits, n as titleHeapLimits, o as titleInputSchema, r as decodeTitleError, s as titleReplySchema } from "./heap-1FYhGds4.mjs";
6
6
  import { constants } from "node:fs";
7
7
  import { lstat, open, opendir, readFile, readdir, realpath, stat } from "node:fs/promises";
@@ -313,22 +313,64 @@ function readTitleRecord(text, value, harness, titles, replace, sessionId, signa
313
313
  function workflowJournalPath(part) {
314
314
  return /^[^/]+\/[^/]+\/subagents\/workflows\/[^/]+\/journal\.jsonl$/.test(relative(part.root, part.path).replaceAll("\\", "/"));
315
315
  }
316
+ /** `<project>/<session>/subagents/**`: the only place where content shape alone makes a journal. */
317
+ function subagentPath(part) {
318
+ const segments = relative(part.root, part.path).replaceAll("\\", "/").split("/");
319
+ return segments.length > 3 && segments[2] === "subagents";
320
+ }
316
321
  /** Only these parts may read past the per-part metadata bound, so every record can be confirmed. */
317
322
  function workflowJournalPart(part, harness) {
318
323
  return harness === "claude" && workflowJournalPath(part);
319
324
  }
320
325
  const agentIdPattern = /^[A-Za-z0-9._:-]{1,128}$/;
321
- /** Result bodies are opaque; only an exact lifecycle envelope establishes kind. */
326
+ /** Fields of a conversation record. A run-journal record carries none of them (ADR 0279). */
327
+ const transcriptFields = [
328
+ "message",
329
+ "sessionId",
330
+ "session_id",
331
+ "uuid",
332
+ "parentUuid",
333
+ "isSidechain",
334
+ "cwd",
335
+ "version",
336
+ "payload",
337
+ "entrypoint",
338
+ "toolUseResult",
339
+ "attachment",
340
+ "leafUuid",
341
+ "logicalParentUuid"
342
+ ];
343
+ /**
344
+ * A Workflow run-journal record (ADR 0279): an exact `launched` record (only `type`), or
345
+ * `started`, `result` or `failed` with a string agent ID and key and no conversation field.
346
+ * Other fields on those three, such as a label, phase or `toolsUsed`, are allowed; result
347
+ * bodies are opaque.
348
+ */
322
349
  function workflowJournalRecord(value) {
323
- if (!isRecord(value)) return false;
324
- const fields = Object.keys(value).sort().join(",");
325
- if (value["type"] === "launched") return fields === "type";
350
+ if (!isRecord(value) || transcriptFields.some((field) => field in value)) return false;
326
351
  const agentId = value["agentId"];
352
+ if (value["type"] === "launched") return Object.keys(value).join(",") === "type";
327
353
  if (typeof agentId !== "string" || typeof value["key"] !== "string") return false;
328
- if (value["type"] === "failed") return fields === "agentId,key,type" && (agentId === "" || agentIdPattern.test(agentId));
354
+ if (value["type"] === "failed") return agentId === "" || agentIdPattern.test(agentId);
329
355
  if (!agentIdPattern.test(agentId)) return false;
330
- if (value["type"] === "started") return fields === "agentId,key,type" || fields === "agentId,key,label,phase,type" && typeof value["label"] === "string" && typeof value["phase"] === "string";
331
- return value["type"] === "result" && fields === "agentId,key,result,type" && (isRecord(value["result"]) || typeof value["result"] === "string");
356
+ if (value["type"] === "started") return true;
357
+ return value["type"] === "result" && "result" in value;
358
+ }
359
+ /**
360
+ * A run-journal record that names an agent run (ADR 0279): `started` or `result`. A file is a
361
+ * journal only when every record is a run-journal record and at least one is of this kind.
362
+ */
363
+ function workflowJournalAgentRecord(value) {
364
+ return workflowJournalRecord(value) && isRecord(value) && (value["type"] === "started" || value["type"] === "result");
365
+ }
366
+ /**
367
+ * Variant for the gate reviewer (issue #59): an exact `launched` or exact `failed` record. At
368
+ * the journal path, a file made only of these is still a journal, as on `origin/main`.
369
+ */
370
+ function workflowJournalExactRecord(value) {
371
+ if (!isRecord(value) || !workflowJournalRecord(value)) return false;
372
+ const fields = Object.keys(value).sort().join(",");
373
+ return value["type"] === "launched" || value["type"] === "failed" && fields === "agentId,key,type";
332
374
  }
333
375
  //#endregion
334
376
  //#region packages/harnesses/src/metadata/interaction-time.ts
@@ -1847,6 +1889,204 @@ function worldState$1(context, payload) {
1847
1889
  paths(context, nativeField(state, "agents_md"), ["directory"]);
1848
1890
  }
1849
1891
  //#endregion
1892
+ //#region packages/harnesses/src/export/identity.ts
1893
+ /** Issue #59: each rule that refuses a source names itself with a fixed literal. */
1894
+ function unsupported$1(site) {
1895
+ throw new ProcessingError("PROCESSING_UNSUPPORTED", { site });
1896
+ }
1897
+ function nativeId(root, key) {
1898
+ const node = nativeField(root, key);
1899
+ if (node === void 0 || node.kind === "literal" && node.raw === "null") return null;
1900
+ const value = nativeText(node);
1901
+ if (value === void 0) unsupported$1("native-id-not-text");
1902
+ if (!value) unsupported$1("native-id-empty");
1903
+ if (value.length > 1024) unsupported$1("native-id-too-long");
1904
+ for (let index = 0; index < value.length; index++) if (value.charCodeAt(index) < 32) unsupported$1("native-id-control-char");
1905
+ return value;
1906
+ }
1907
+ function sourceInspector(describe, finalize = (identity) => identity, harness) {
1908
+ let value = {
1909
+ role: "unknown",
1910
+ sessionId: null,
1911
+ agentId: null,
1912
+ dispatchParentSessionId: null,
1913
+ fork: null
1914
+ };
1915
+ let failed = false;
1916
+ let finished = false;
1917
+ let version;
1918
+ function reject(site, cause) {
1919
+ failed = true;
1920
+ throw new ProcessingError("PROCESSING_UNSUPPORTED", {
1921
+ ...cause === void 0 ? {} : { cause },
1922
+ site: processingSite(cause)?.site ?? site,
1923
+ ...harness ? { harness: harness.harness } : {},
1924
+ ...version === void 0 ? {} : { harnessVersion: version }
1925
+ });
1926
+ }
1927
+ function merge(previous, current, site) {
1928
+ if (previous !== null && current !== null && previous !== current) reject(site);
1929
+ return current ?? previous;
1930
+ }
1931
+ function current() {
1932
+ if (failed) reject("source-already-refused");
1933
+ return Object.freeze(finalize(value));
1934
+ }
1935
+ return {
1936
+ inspect(root) {
1937
+ if (failed) reject("source-already-refused");
1938
+ if (finished) reject("source-read-after-finish");
1939
+ version = harness?.version(root) ?? version;
1940
+ try {
1941
+ const next = describe(root);
1942
+ if (value.role !== "unknown" && next.role !== "unknown" && value.role !== next.role) reject("source-role-conflict");
1943
+ if (value.fork !== null && next.fork !== null && JSON.stringify(value.fork) !== JSON.stringify(next.fork)) reject("source-fork-conflict");
1944
+ value = {
1945
+ role: next.role === "unknown" ? value.role : next.role,
1946
+ sessionId: merge(value.sessionId, next.sessionId, "source-session-id-conflict"),
1947
+ agentId: merge(value.agentId, next.agentId, "source-agent-id-conflict"),
1948
+ dispatchParentSessionId: merge(value.dispatchParentSessionId, next.dispatchParentSessionId, "source-parent-id-conflict"),
1949
+ fork: next.fork ?? value.fork
1950
+ };
1951
+ } catch (error) {
1952
+ reject("source-describe-failed", error);
1953
+ }
1954
+ },
1955
+ current,
1956
+ finish() {
1957
+ finished = true;
1958
+ return current();
1959
+ }
1960
+ };
1961
+ }
1962
+ function unknownIdentity() {
1963
+ return {
1964
+ role: "unknown",
1965
+ sessionId: null,
1966
+ agentId: null,
1967
+ dispatchParentSessionId: null,
1968
+ fork: null
1969
+ };
1970
+ }
1971
+ //#endregion
1972
+ //#region packages/harnesses/src/export/claude.ts
1973
+ const nativeTypes = /* @__PURE__ */ new Set([
1974
+ "user",
1975
+ "assistant",
1976
+ "system",
1977
+ "progress",
1978
+ "summary",
1979
+ "result",
1980
+ "queue-operation",
1981
+ "custom-title",
1982
+ "compact-boundary",
1983
+ "last-prompt",
1984
+ "file-history-snapshot"
1985
+ ]);
1986
+ /** Standalone metadata lines: a summary names a leaf, a snapshot lists file backups. */
1987
+ const metadataTypes$1 = /* @__PURE__ */ new Set(["summary", "file-history-snapshot"]);
1988
+ function claudeMetadataRecord(root) {
1989
+ return metadataTypes$1.has(nativeText(nativeField(root, "type")) ?? "");
1990
+ }
1991
+ function claudeIdentityType(type) {
1992
+ return typeof type === "string" && nativeTypes.has(type);
1993
+ }
1994
+ function claudeIdentityRecord(root) {
1995
+ return claudeIdentityType(nativeText(nativeField(root, "type")));
1996
+ }
1997
+ function claudeIdentity(root) {
1998
+ if (!claudeIdentityRecord(root)) return unknownIdentity();
1999
+ const marker = nativeField(root, "isSidechain");
2000
+ const sidechain = nativeBoolean(marker);
2001
+ if (marker !== void 0 && sidechain === void 0) throw new ProcessingError("PROCESSING_UNSUPPORTED", { site: "claude-sidechain-not-boolean" });
2002
+ const sessionId = nativeId(root, "sessionId");
2003
+ return {
2004
+ role: sidechain === void 0 ? "unknown" : sidechain ? "worker" : "main",
2005
+ sessionId,
2006
+ agentId: nativeId(root, "agentId"),
2007
+ dispatchParentSessionId: sidechain === true ? sessionId : null,
2008
+ fork: null
2009
+ };
2010
+ }
2011
+ function claudeRelations(root) {
2012
+ const type = nativeText(nativeField(root, "type"));
2013
+ if (type !== "assistant" && type !== "user") return [];
2014
+ const message = nativeField(root, "message");
2015
+ const role = nativeText(nativeField(message, "role"));
2016
+ if (role !== void 0 && role !== type) return [];
2017
+ const relations = [];
2018
+ for (const item of nativeItems(nativeField(message, "content"))) {
2019
+ const blockType = nativeText(nativeField(item, "type"));
2020
+ const kind = type === "assistant" && blockType === "tool_use" ? "call" : type === "user" && blockType === "tool_result" ? "result" : void 0;
2021
+ if (kind === void 0) continue;
2022
+ const callId = nativeId(item, kind === "call" ? "id" : "tool_use_id");
2023
+ if (callId !== null) relations.push(Object.freeze({
2024
+ kind,
2025
+ callId
2026
+ }));
2027
+ }
2028
+ return Object.freeze(relations);
2029
+ }
2030
+ const sessionFile = /(?:^|[/\\])([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl(?:\.zst)?$/i;
2031
+ /** Claude names a session transcript `<sessionId>.jsonl`; other layouts name no own session. */
2032
+ function claudeOwnSessionId(location) {
2033
+ return sessionFile.exec(location?.path ?? "")?.[1] ?? null;
2034
+ }
2035
+ /**
2036
+ * A main log may embed workers; a worker log never establishes a new root. A side conversation
2037
+ * copies another session's records (with that session's ID) into its own `<sessionId>.jsonl` and
2038
+ * names its own ID only later, so the first native ID decides: when it is not the file's own ID,
2039
+ * the source is that session's fork and keeps its own ID (ADR 0187). Header and complete reads
2040
+ * therefore agree; a third ID, or a foreign ID after the source named itself, stays unsupported.
2041
+ */
2042
+ function claudeSourceInspector(location) {
2043
+ const own = claudeOwnSessionId(location);
2044
+ let main = false;
2045
+ let first = null;
2046
+ return sourceInspector((root) => {
2047
+ let identity = claudeIdentity(root);
2048
+ if (identity.role === "main") main = true;
2049
+ if (main && identity.role === "worker") identity = {
2050
+ ...identity,
2051
+ role: "main",
2052
+ agentId: null,
2053
+ dispatchParentSessionId: null
2054
+ };
2055
+ if (own === null || identity.role === "worker" || identity.sessionId === null) return identity;
2056
+ first ??= identity.sessionId;
2057
+ if (identity.sessionId === own) return {
2058
+ ...identity,
2059
+ sessionId: null
2060
+ };
2061
+ if (first === own) throw new ProcessingError("PROCESSING_UNSUPPORTED", { site: "claude-foreign-session-after-own" });
2062
+ return identity;
2063
+ }, (identity) => {
2064
+ if (identity.role === "worker" || own === null || first === null) return {
2065
+ ...identity,
2066
+ dispatchParentSessionId: identity.role === "worker" ? identity.sessionId : null
2067
+ };
2068
+ return {
2069
+ ...identity,
2070
+ sessionId: own,
2071
+ dispatchParentSessionId: null,
2072
+ fork: first === own ? null : Object.freeze({
2073
+ parentSessionId: first,
2074
+ position: Object.freeze({
2075
+ kind: "unknown",
2076
+ value: null
2077
+ })
2078
+ })
2079
+ };
2080
+ }, {
2081
+ harness: "claude",
2082
+ version: claudeVersion
2083
+ });
2084
+ }
2085
+ /** The Claude Code version a record declares, when it has the shape of a version. */
2086
+ function claudeVersion(root) {
2087
+ return declaredVersion(nativeText(nativeField(root, "version")));
2088
+ }
2089
+ //#endregion
1850
2090
  //#region packages/harnesses/src/retention/output.ts
1851
2091
  function outputTags(family, failed) {
1852
2092
  return {
@@ -1996,6 +2236,7 @@ var ClaudeAssociation = class {
1996
2236
  scope = "main";
1997
2237
  unknownSidechain;
1998
2238
  invalid = false;
2239
+ version;
1999
2240
  options;
2000
2241
  constructor(options) {
2001
2242
  this.options = options;
@@ -2003,19 +2244,20 @@ var ClaudeAssociation = class {
2003
2244
  if (!Number.isSafeInteger(this.maximum) || this.maximum < 1 || this.maximum > 1e5) throw new ProcessingError("PROCESSING_LIMIT");
2004
2245
  }
2005
2246
  checkSource(root) {
2006
- if (this.invalid) throw new ProcessingError("PROCESSING_UNSUPPORTED");
2247
+ if (this.invalid) this.rejectSource("claude-retention-already-refused");
2248
+ this.version = claudeVersion(root) ?? this.version;
2007
2249
  const sidechain = nativeField(root, "isSidechain");
2008
2250
  const value = nativeBoolean(sidechain);
2009
- if (sidechain && value === void 0) this.rejectSource();
2010
- if (this.options.sourceRole === "worker" && value === false) this.rejectSource();
2251
+ if (sidechain && value === void 0) this.rejectSource("claude-retention-sidechain-not-boolean");
2252
+ if (this.options.sourceRole === "worker" && value === false) this.rejectSource("claude-retention-worker-not-sidechain");
2011
2253
  if (this.options.sourceRole === "unknown" && value !== void 0) {
2012
- if (this.unknownSidechain !== void 0 && this.unknownSidechain !== value) this.rejectSource();
2254
+ if (this.unknownSidechain !== void 0 && this.unknownSidechain !== value) this.rejectSource("claude-retention-sidechain-changed");
2013
2255
  this.unknownSidechain = value;
2014
2256
  }
2015
2257
  const own = this.options.nativeSessionId;
2016
2258
  const sessionId = this.identity(root, "sessionId", void 0);
2017
2259
  if (sessionId !== void 0 && this.sessionId !== void 0 && sessionId !== this.sessionId) {
2018
- if (!own || sessionId !== own) this.rejectSource();
2260
+ if (!own || sessionId !== own) this.rejectSource("claude-retention-session-id-changed");
2019
2261
  }
2020
2262
  this.sessionId = sessionId ?? this.sessionId;
2021
2263
  if (this.options.sourceRole === "worker") this.agentId = this.identity(root, "agentId", this.agentId);
@@ -2027,12 +2269,18 @@ var ClaudeAssociation = class {
2027
2269
  const node = nativeField(root, key);
2028
2270
  if (!node) return previous;
2029
2271
  const value = nativeText(node);
2030
- if (!value || value.length > 1024 || previous !== void 0 && previous !== value) this.rejectSource();
2272
+ if (!value) this.rejectSource("claude-retention-id-empty");
2273
+ if (value.length > 1024) this.rejectSource("claude-retention-id-too-long");
2274
+ if (previous !== void 0 && previous !== value) this.rejectSource("claude-retention-agent-id-changed");
2031
2275
  return value;
2032
2276
  }
2033
- rejectSource() {
2277
+ rejectSource(site) {
2034
2278
  this.invalid = true;
2035
- throw new ProcessingError("PROCESSING_UNSUPPORTED");
2279
+ throw new ProcessingError("PROCESSING_UNSUPPORTED", {
2280
+ site,
2281
+ harness: "claude",
2282
+ ...this.version === void 0 ? {} : { harnessVersion: this.version }
2283
+ });
2036
2284
  }
2037
2285
  register(block, selection) {
2038
2286
  const id = nativeText(nativeField(block, "id"));
@@ -2527,7 +2775,7 @@ var ClaudeSkills = class {
2527
2775
  };
2528
2776
  //#endregion
2529
2777
  //#region packages/harnesses/src/retention/claude/index.ts
2530
- const metadataTypes$1 = /* @__PURE__ */ new Set([
2778
+ const metadataTypes = /* @__PURE__ */ new Set([
2531
2779
  "system",
2532
2780
  "progress",
2533
2781
  "summary",
@@ -2554,7 +2802,7 @@ function createClaudeRetention(options) {
2554
2802
  "worker",
2555
2803
  "guardian",
2556
2804
  "unknown"
2557
- ].includes(options.sourceRole)) throw new ProcessingError("PROCESSING_UNSUPPORTED");
2805
+ ].includes(options.sourceRole)) throw new ProcessingError("PROCESSING_UNSUPPORTED", { site: "claude-retention-role-unknown" });
2558
2806
  const settings = { ...options };
2559
2807
  const association = new ClaudeAssociation(settings);
2560
2808
  const skills = new ClaudeSkills(settings);
@@ -2577,7 +2825,7 @@ function createClaudeRetention(options) {
2577
2825
  }
2578
2826
  selection.injected(nativeField(root, "wireToolInputs"), "derived");
2579
2827
  if (type === "attachment") return attachment(root, recordSettings.sourceRole === "worker", selection);
2580
- if (!type || !metadataTypes$1.has(type) && type !== "user" && type !== "assistant") {
2828
+ if (!type || !metadataTypes.has(type) && type !== "user" && type !== "assistant") {
2581
2829
  selection.diagnose("unsupported-native-shape", root);
2582
2830
  const result = selection.result();
2583
2831
  let tolerated = false;
@@ -3001,13 +3249,15 @@ function content(context, node, role, mode, userAttachments = false) {
3001
3249
  });
3002
3250
  return;
3003
3251
  }
3004
- const marked = userAttachments && role === "main-text" ? pastedAttachments(SourceText.original(node.value))?.text : void 0;
3252
+ const marked = userAttachments && role === "main-text" ? pastedAttachments(SourceText.original(node.value)) : void 0;
3005
3253
  if (marked !== void 0) {
3006
3254
  context.edits.push({
3007
3255
  nodeId: node.id,
3008
3256
  role,
3009
3257
  mode: { kind: "remove" },
3010
- replacement: marked
3258
+ replacement: marked.text,
3259
+ replacementSourceRanges: marked.ranges,
3260
+ replacementTargetRanges: marked.targetRanges
3011
3261
  });
3012
3262
  return;
3013
3263
  }
@@ -3475,72 +3725,6 @@ function worldState(context, payload) {
3475
3725
  }
3476
3726
  }
3477
3727
  //#endregion
3478
- //#region packages/harnesses/src/export/identity.ts
3479
- function nativeId(root, key) {
3480
- const node = nativeField(root, key);
3481
- if (node === void 0 || node.kind === "literal" && node.raw === "null") return null;
3482
- const value = nativeText(node);
3483
- if (!value || value.length > 1024) throw new ProcessingError("PROCESSING_UNSUPPORTED");
3484
- for (let index = 0; index < value.length; index++) if (value.charCodeAt(index) < 32) throw new ProcessingError("PROCESSING_UNSUPPORTED");
3485
- return value;
3486
- }
3487
- function sourceInspector(describe, finalize = (identity) => identity) {
3488
- let value = {
3489
- role: "unknown",
3490
- sessionId: null,
3491
- agentId: null,
3492
- dispatchParentSessionId: null,
3493
- fork: null
3494
- };
3495
- let failed = false;
3496
- let finished = false;
3497
- function reject(cause) {
3498
- failed = true;
3499
- throw new ProcessingError("PROCESSING_UNSUPPORTED", { cause });
3500
- }
3501
- function merge(previous, current) {
3502
- if (previous !== null && current !== null && previous !== current) reject();
3503
- return current ?? previous;
3504
- }
3505
- function current() {
3506
- if (failed) reject();
3507
- return Object.freeze(finalize(value));
3508
- }
3509
- return {
3510
- inspect(root) {
3511
- if (failed || finished) reject();
3512
- try {
3513
- const next = describe(root);
3514
- if (value.role !== "unknown" && next.role !== "unknown" && value.role !== next.role) reject();
3515
- if (value.fork !== null && next.fork !== null && JSON.stringify(value.fork) !== JSON.stringify(next.fork)) reject();
3516
- value = {
3517
- role: next.role === "unknown" ? value.role : next.role,
3518
- sessionId: merge(value.sessionId, next.sessionId),
3519
- agentId: merge(value.agentId, next.agentId),
3520
- dispatchParentSessionId: merge(value.dispatchParentSessionId, next.dispatchParentSessionId),
3521
- fork: next.fork ?? value.fork
3522
- };
3523
- } catch (error) {
3524
- reject(error);
3525
- }
3526
- },
3527
- current,
3528
- finish() {
3529
- finished = true;
3530
- return current();
3531
- }
3532
- };
3533
- }
3534
- function unknownIdentity() {
3535
- return {
3536
- role: "unknown",
3537
- sessionId: null,
3538
- agentId: null,
3539
- dispatchParentSessionId: null,
3540
- fork: null
3541
- };
3542
- }
3543
- //#endregion
3544
3728
  //#region packages/harnesses/src/export/codex.ts
3545
3729
  function codexIdentity(root) {
3546
3730
  if (nativeText(nativeField(root, "type")) !== "session_meta") return unknownIdentity();
@@ -3550,7 +3734,7 @@ function codexIdentity(root) {
3550
3734
  const subagent = nativeField(source, "subagent");
3551
3735
  const spawn = nativeField(subagent, "thread_spawn");
3552
3736
  const guardian = nativeText(nativeField(subagent, "other")) === "guardian";
3553
- if (guardian && spawn !== void 0) throw new ProcessingError("PROCESSING_UNSUPPORTED");
3737
+ if (guardian && spawn !== void 0) throw new ProcessingError("PROCESSING_UNSUPPORTED", { site: "codex-guardian-thread-spawn" });
3554
3738
  const role = source?.kind === "string" && [
3555
3739
  "cli",
3556
3740
  "vscode",
@@ -3626,8 +3810,16 @@ function codexSourceInspector() {
3626
3810
  if (seenMetadata || nativeText(nativeField(root, "type")) !== "session_meta") return unknownIdentity();
3627
3811
  seenMetadata = true;
3628
3812
  return codexIdentity(root);
3813
+ }, void 0, {
3814
+ harness: "codex",
3815
+ version: codexVersion
3629
3816
  });
3630
3817
  }
3818
+ /** The Codex CLI version a session_meta record declares, when it has the shape of a version. */
3819
+ function codexVersion(root) {
3820
+ if (nativeText(nativeField(root, "type")) !== "session_meta") return void 0;
3821
+ return declaredVersion(nativeText(nativeField(nativeField(root, "payload"), "cli_version")));
3822
+ }
3631
3823
  //#endregion
3632
3824
  //#region packages/harnesses/src/retention/codex/index.ts
3633
3825
  function nativeItem(context, node, calls) {
@@ -3675,7 +3867,10 @@ function createCodexRetention(options) {
3675
3867
  if (type === "session_meta" && payload?.kind === "object") {
3676
3868
  if (codexIdentity(root).role === "unknown") unsupported(context, payload);
3677
3869
  const physicalRole = inspector.current().role;
3678
- if (sourceRole !== "unknown" && physicalRole !== "unknown" && physicalRole !== sourceRole) throw new ProcessingError("PROCESSING_UNSUPPORTED");
3870
+ if (sourceRole !== "unknown" && physicalRole !== "unknown" && physicalRole !== sourceRole) throw new ProcessingError("PROCESSING_UNSUPPORTED", {
3871
+ site: "codex-retention-role-mismatch",
3872
+ harness: "codex"
3873
+ });
3679
3874
  const id = fieldText(payload, "id");
3680
3875
  if (!id || id.length > 512) unsupported(context, payload);
3681
3876
  }
@@ -3716,117 +3911,6 @@ function createCodexRetention(options) {
3716
3911
  };
3717
3912
  }
3718
3913
  //#endregion
3719
- //#region packages/harnesses/src/export/claude.ts
3720
- const nativeTypes = /* @__PURE__ */ new Set([
3721
- "user",
3722
- "assistant",
3723
- "system",
3724
- "progress",
3725
- "summary",
3726
- "result",
3727
- "queue-operation",
3728
- "custom-title",
3729
- "compact-boundary",
3730
- "last-prompt",
3731
- "file-history-snapshot"
3732
- ]);
3733
- /** Standalone metadata lines: a summary names a leaf, a snapshot lists file backups. */
3734
- const metadataTypes = /* @__PURE__ */ new Set(["summary", "file-history-snapshot"]);
3735
- function claudeMetadataRecord(root) {
3736
- return metadataTypes.has(nativeText(nativeField(root, "type")) ?? "");
3737
- }
3738
- function claudeIdentityType(type) {
3739
- return typeof type === "string" && nativeTypes.has(type);
3740
- }
3741
- function claudeIdentityRecord(root) {
3742
- return claudeIdentityType(nativeText(nativeField(root, "type")));
3743
- }
3744
- function claudeIdentity(root) {
3745
- if (!claudeIdentityRecord(root)) return unknownIdentity();
3746
- const marker = nativeField(root, "isSidechain");
3747
- const sidechain = nativeBoolean(marker);
3748
- if (marker !== void 0 && sidechain === void 0) throw new ProcessingError("PROCESSING_UNSUPPORTED");
3749
- const sessionId = nativeId(root, "sessionId");
3750
- return {
3751
- role: sidechain === void 0 ? "unknown" : sidechain ? "worker" : "main",
3752
- sessionId,
3753
- agentId: nativeId(root, "agentId"),
3754
- dispatchParentSessionId: sidechain === true ? sessionId : null,
3755
- fork: null
3756
- };
3757
- }
3758
- function claudeRelations(root) {
3759
- const type = nativeText(nativeField(root, "type"));
3760
- if (type !== "assistant" && type !== "user") return [];
3761
- const message = nativeField(root, "message");
3762
- const role = nativeText(nativeField(message, "role"));
3763
- if (role !== void 0 && role !== type) return [];
3764
- const relations = [];
3765
- for (const item of nativeItems(nativeField(message, "content"))) {
3766
- const blockType = nativeText(nativeField(item, "type"));
3767
- const kind = type === "assistant" && blockType === "tool_use" ? "call" : type === "user" && blockType === "tool_result" ? "result" : void 0;
3768
- if (kind === void 0) continue;
3769
- const callId = nativeId(item, kind === "call" ? "id" : "tool_use_id");
3770
- if (callId !== null) relations.push(Object.freeze({
3771
- kind,
3772
- callId
3773
- }));
3774
- }
3775
- return Object.freeze(relations);
3776
- }
3777
- const sessionFile = /(?:^|[/\\])([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl(?:\.zst)?$/i;
3778
- /** Claude names a session transcript `<sessionId>.jsonl`; other layouts name no own session. */
3779
- function claudeOwnSessionId(location) {
3780
- return sessionFile.exec(location?.path ?? "")?.[1] ?? null;
3781
- }
3782
- /**
3783
- * A main log may embed workers; a worker log never establishes a new root. A side conversation
3784
- * copies another session's records (with that session's ID) into its own `<sessionId>.jsonl` and
3785
- * names its own ID only later, so the first native ID decides: when it is not the file's own ID,
3786
- * the source is that session's fork and keeps its own ID (ADR 0187). Header and complete reads
3787
- * therefore agree; a third ID, or a foreign ID after the source named itself, stays unsupported.
3788
- */
3789
- function claudeSourceInspector(location) {
3790
- const own = claudeOwnSessionId(location);
3791
- let main = false;
3792
- let first = null;
3793
- return sourceInspector((root) => {
3794
- let identity = claudeIdentity(root);
3795
- if (identity.role === "main") main = true;
3796
- if (main && identity.role === "worker") identity = {
3797
- ...identity,
3798
- role: "main",
3799
- agentId: null,
3800
- dispatchParentSessionId: null
3801
- };
3802
- if (own === null || identity.role === "worker" || identity.sessionId === null) return identity;
3803
- first ??= identity.sessionId;
3804
- if (identity.sessionId === own) return {
3805
- ...identity,
3806
- sessionId: null
3807
- };
3808
- if (first === own) throw new ProcessingError("PROCESSING_UNSUPPORTED");
3809
- return identity;
3810
- }, (identity) => {
3811
- if (identity.role === "worker" || own === null || first === null) return {
3812
- ...identity,
3813
- dispatchParentSessionId: identity.role === "worker" ? identity.sessionId : null
3814
- };
3815
- return {
3816
- ...identity,
3817
- sessionId: own,
3818
- dispatchParentSessionId: null,
3819
- fork: first === own ? null : Object.freeze({
3820
- parentSessionId: first,
3821
- position: Object.freeze({
3822
- kind: "unknown",
3823
- value: null
3824
- })
3825
- })
3826
- };
3827
- });
3828
- }
3829
- //#endregion
3830
3914
  //#region packages/harnesses/src/export/annotations.ts
3831
3915
  /** Exact fork metadata confirmed by bounded native-header inspection. */
3832
3916
  function codexAnnotations(root, options, selection) {
@@ -4381,7 +4465,9 @@ function createNativeAdapters() {
4381
4465
  relations: claudeRelations,
4382
4466
  lifecycle: Object.freeze({
4383
4467
  part: workflowJournalPath,
4384
- record: workflowJournalRecord
4468
+ record: workflowJournalRecord,
4469
+ agentRecord: workflowJournalAgentRecord,
4470
+ exactRecord: workflowJournalExactRecord
4385
4471
  }),
4386
4472
  metadataRecord: claudeMetadataRecord,
4387
4473
  launch: claudeLaunch,
@@ -4616,8 +4702,10 @@ async function metadataStream(part, harness, options) {
4616
4702
  let incompleteRecord = false;
4617
4703
  let unchanged = false;
4618
4704
  let lastInteractionAt = null;
4619
- let journalOnly = journalPath;
4705
+ let journalOnly = harness === "claude" && (journalPath || subagentPath(part));
4706
+ let journalAgentRecords = 0;
4620
4707
  let journalRecords = 0;
4708
+ let journalExactOnly = true;
4621
4709
  let launch;
4622
4710
  let launchKnown = false;
4623
4711
  let headExhausted = false;
@@ -4636,14 +4724,16 @@ async function metadataStream(part, harness, options) {
4636
4724
  try {
4637
4725
  const text = decoder.decode(line);
4638
4726
  const value = JSON.parse(text);
4639
- if (journalPath && workflowJournalRecord(value)) {
4727
+ if ((journalPath || journalOnly) && workflowJournalRecord(value)) {
4640
4728
  parseNativeRecord(text, {
4641
4729
  ...defaultLimits,
4642
4730
  maxRecordBytes: limit,
4643
4731
  maxUnitBytes: limit,
4644
4732
  maxDepth: discoveryBounds.depth
4645
4733
  }, options.signal ?? new AbortController().signal);
4734
+ if (workflowJournalAgentRecord(value)) journalAgentRecords++;
4646
4735
  journalRecords++;
4736
+ if (!workflowJournalExactRecord(value)) journalExactOnly = false;
4647
4737
  return;
4648
4738
  }
4649
4739
  journalOnly = false;
@@ -4728,7 +4818,7 @@ async function metadataStream(part, harness, options) {
4728
4818
  }
4729
4819
  checkDiscoveryAbort(options.signal);
4730
4820
  if (headExhausted && !launchKnown && readMode === "append") launch = await probeLaunch(part, harness, options.signal);
4731
- const workflowJournal = journalOnly && journalRecords > 0 && !stopped;
4821
+ const workflowJournal = journalOnly && (journalAgentRecords > 0 || journalPath && journalExactOnly && journalRecords > 0) && !stopped && !incompleteRecord;
4732
4822
  const complete = !stopped && !incompleteRecord && unchanged;
4733
4823
  if (journalPath && !workflowJournal) warnings.add("UNCONFIRMED_WORKFLOW_JOURNAL");
4734
4824
  if (launch === "automation") warnings.add("NON_INTERACTIVE_LAUNCH");