@useorgx/wizard 0.1.56 → 0.1.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  // src/cli.ts
4
4
 
5
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="02842523-d681-5cea-96aa-0f0c896e9fb5")}catch(e){}}();
5
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="8eca99e4-958f-5704-a1cb-bd96bbd10293")}catch(e){}}();
6
6
  import * as clack from "@clack/prompts";
7
7
  import { spawnSync as spawnSync5 } from "child_process";
8
- import { readFileSync as readFileSync8 } from "fs";
8
+ import { readFileSync as readFileSync10 } from "fs";
9
9
  import { hostname } from "os";
10
- import { resolve as resolve3 } from "path";
10
+ import { resolve as resolve4 } from "path";
11
11
  import { Command } from "commander";
12
12
  import pc3 from "picocolors";
13
13
 
@@ -187,6 +187,9 @@ import {
187
187
  writeFileSync
188
188
  } from "fs";
189
189
  import { dirname } from "path";
190
+ function fileExists(path) {
191
+ return existsSync(path);
192
+ }
190
193
  function readTextIfExists(path) {
191
194
  if (!existsSync(path)) return null;
192
195
  try {
@@ -831,7 +834,7 @@ function parsePairingPollResult(value) {
831
834
  };
832
835
  }
833
836
  function sleep(ms) {
834
- return new Promise((resolve4) => setTimeout(resolve4, ms));
837
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
835
838
  }
836
839
  async function startBrowserPairing(options, fetchImpl) {
837
840
  const data = await fetchJson({
@@ -944,12 +947,12 @@ h1{font-size:1.5rem;color:#b91c1c}p{color:#555;margin-top:.5rem}</style>
944
947
  <p>Return to your terminal and try again.</p>
945
948
  </div></body></html>`;
946
949
  function tryListen(port, hostname2) {
947
- return new Promise((resolve4, reject) => {
950
+ return new Promise((resolve5, reject) => {
948
951
  const server = createServer();
949
952
  server.once("error", reject);
950
953
  server.listen(port, hostname2, () => {
951
954
  server.removeListener("error", reject);
952
- resolve4(server);
955
+ resolve5(server);
953
956
  });
954
957
  });
955
958
  }
@@ -978,7 +981,7 @@ async function startLocalAuthServer(options) {
978
981
  const successHtml = options.successHtml ?? DEFAULT_SUCCESS_HTML;
979
982
  const errorHtml = options.errorHtml ?? DEFAULT_ERROR_HTML;
980
983
  const { server, port } = await bindServer(options.preferredPort, hostname2);
981
- const result = new Promise((resolve4, reject) => {
984
+ const result = new Promise((resolve5, reject) => {
982
985
  const timer = setTimeout(() => {
983
986
  server.close();
984
987
  reject(new Error("Timed out waiting for browser authorization."));
@@ -1021,7 +1024,7 @@ async function startLocalAuthServer(options) {
1021
1024
  res.writeHead(200, { "Content-Type": "text/html" }).end(successHtml);
1022
1025
  clearTimeout(timer);
1023
1026
  server.close();
1024
- resolve4({ code, state });
1027
+ resolve5({ code, state });
1025
1028
  });
1026
1029
  });
1027
1030
  return { port, result };
@@ -3604,8 +3607,8 @@ function encodeRepoPath2(value) {
3604
3607
  return value.split("/").filter((segment) => segment.length > 0).map((segment) => encodeURIComponent(segment)).join("/");
3605
3608
  }
3606
3609
  function isLikelyRepoFilePath(path) {
3607
- const basename5 = path.split("/").pop() ?? path;
3608
- return basename5.includes(".") && !/^\.[^./]+$/.test(basename5);
3610
+ const basename6 = path.split("/").pop() ?? path;
3611
+ return basename6.includes(".") && !/^\.[^./]+$/.test(basename6);
3609
3612
  }
3610
3613
  function buildContentsUrl2(spec, path) {
3611
3614
  const encodedPath = encodeRepoPath2(path);
@@ -3881,7 +3884,7 @@ function formatCommandFailure(command, args, result) {
3881
3884
  return `${command} ${args.join(" ")} failed${result.exitCode >= 0 ? ` with exit code ${result.exitCode}` : ""}${detail ? `: ${detail}` : "."}`;
3882
3885
  }
3883
3886
  async function defaultCommandRunner(command, args) {
3884
- return await new Promise((resolve4) => {
3887
+ return await new Promise((resolve5) => {
3885
3888
  const child = spawn(command, [...args], {
3886
3889
  env: process.env,
3887
3890
  stdio: ["ignore", "pipe", "pipe"]
@@ -3895,16 +3898,16 @@ async function defaultCommandRunner(command, args) {
3895
3898
  stderr += chunk.toString();
3896
3899
  });
3897
3900
  child.on("error", (error) => {
3898
- const errorCode = typeof error === "object" && error && "code" in error ? String(error.code) : void 0;
3899
- resolve4({
3901
+ const errorCode2 = typeof error === "object" && error && "code" in error ? String(error.code) : void 0;
3902
+ resolve5({
3900
3903
  exitCode: -1,
3901
3904
  stdout,
3902
3905
  stderr,
3903
- ...errorCode ? { errorCode } : {}
3906
+ ...errorCode2 ? { errorCode: errorCode2 } : {}
3904
3907
  });
3905
3908
  });
3906
3909
  child.on("close", (code) => {
3907
- resolve4({
3910
+ resolve5({
3908
3911
  exitCode: code ?? -1,
3909
3912
  stdout,
3910
3913
  stderr
@@ -6211,7 +6214,7 @@ function initializeWizardSentry() {
6211
6214
  Sentry.init({
6212
6215
  dsn,
6213
6216
  environment: process.env.ORGX_SENTRY_ENVIRONMENT || "production",
6214
- release: "useorgx-wizard@0.1.56",
6217
+ release: "useorgx-wizard@0.1.57",
6215
6218
  tracesSampleRate: sampleRate(process.env.ORGX_SENTRY_TRACES_SAMPLE_RATE),
6216
6219
  enableLogs: true,
6217
6220
  sendDefaultPii: false,
@@ -13623,13 +13626,758 @@ function buildWorkGraphHookReplayPatch(readResult) {
13623
13626
  }
13624
13627
 
13625
13628
  // src/lib/runtime-hooks.ts
13626
- import { copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
13629
+ import { copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync6 } from "fs";
13627
13630
  import { homedir as homedir3 } from "os";
13628
- import { dirname as dirname5, join as join8 } from "path";
13631
+ import { dirname as dirname5, join as join8, resolve as resolve2 } from "path";
13632
+
13633
+ // src/lib/session-summary-hook.ts
13634
+ var SESSION_SUMMARY_HOOK_MARKER = "orgx-session-summary.mjs";
13635
+ var SESSION_SUMMARY_ENDPOINT_PATH = "/api/v1/sessions/summary";
13636
+ var SESSION_SUMMARY_FALLBACK_ENDPOINT_PATH = "/api/v1/work-graph/reports";
13637
+ var SESSION_SUMMARY_SCHEMA_VERSION = "2026-08-07";
13638
+ var HOOK_OUTBOX_MAX_BYTES = 8 * 1024 * 1024;
13639
+ function buildSessionSummaryHookScriptContent() {
13640
+ return `#!/usr/bin/env node
13641
+ // OrgX lean session-summary hook. Generated by @useorgx/wizard.
13642
+ //
13643
+ // Hot path (PostToolUse and friends): ONE small read-modify-write. No network.
13644
+ // Terminal (Stop / SessionEnd): ONE compact cumulative summary queue write.
13645
+ // A separate orgx-wizard hooks flush process owns all network delivery.
13646
+ //
13647
+ // NOTE ON Stop SEMANTICS: in Claude Code, Stop fires at every assistant turn
13648
+ // boundary, not only once at session end. Stop therefore writes a cumulative
13649
+ // snapshot and retains state. The ingest endpoint merges repeated snapshots by
13650
+ // session_id; SessionEnd, when the client sends it, queues the final snapshot
13651
+ // and clears local state.
13652
+
13653
+ import { spawn } from "node:child_process";
13654
+ import { closeSync, fsyncSync, mkdirSync, openSync, readFileSync, writeFileSync, writeSync, rmSync, renameSync } from "node:fs";
13655
+ import { homedir } from "node:os";
13656
+ import { join, dirname, basename } from "node:path";
13657
+
13658
+ var SCHEMA_VERSION = "${SESSION_SUMMARY_SCHEMA_VERSION}";
13659
+
13660
+ export function parseArgs(argv) {
13661
+ var args = {};
13662
+ for (var i = 0; i < argv.length; i++) {
13663
+ var arg = argv[i];
13664
+ if (arg.indexOf("--") !== 0) continue;
13665
+ var eq = arg.indexOf("=");
13666
+ if (eq < 0) args[arg.slice(2)] = "true";
13667
+ else args[arg.slice(2, eq)] = arg.slice(eq + 1);
13668
+ }
13669
+ return args;
13670
+ }
13671
+
13672
+ export function pickString() {
13673
+ for (var i = 0; i < arguments.length; i++) {
13674
+ var value = arguments[i];
13675
+ if (typeof value !== "string") continue;
13676
+ var trimmed = value.trim();
13677
+ if (trimmed) return trimmed;
13678
+ }
13679
+ return undefined;
13680
+ }
13681
+
13682
+ function configHome() {
13683
+ var xdg = pickString(process.env.XDG_CONFIG_HOME);
13684
+ return xdg ? xdg : join(homedir(), ".config");
13685
+ }
13686
+
13687
+ function defaultStateDir() {
13688
+ var configured = pickString(process.env.ORGX_WIZARD_CONFIG_HOME);
13689
+ var root = configured ? configured : join(configHome(), "useorgx", "wizard");
13690
+ return join(root, "sessions");
13691
+ }
13692
+
13693
+ function defaultQueueDir() {
13694
+ var configured = pickString(process.env.ORGX_WIZARD_CONFIG_HOME);
13695
+ var root = configured ? configured : join(configHome(), "useorgx", "wizard");
13696
+ return join(root, "session-summary-captures");
13697
+ }
13698
+
13699
+ // Mirrors the backfill distiller so live and historical records agree on repo.
13700
+ export function repoOf(cwd) {
13701
+ if (!cwd) return "unknown";
13702
+ var marker = "/Code/";
13703
+ var at = cwd.indexOf(marker);
13704
+ if (at >= 0) {
13705
+ var rest = cwd.slice(at + marker.length);
13706
+ var slash = rest.indexOf("/");
13707
+ var name = slash < 0 ? rest : rest.slice(0, slash);
13708
+ if (name) return name;
13709
+ }
13710
+ return basename(cwd) || "unknown";
13711
+ }
13712
+
13713
+ export function statePath(sessionId, dir) {
13714
+ var root = dir ? dir : defaultStateDir();
13715
+ // Session ids come from the harness; keep them filesystem-safe regardless.
13716
+ var safe = String(sessionId).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 128);
13717
+ return join(root, safe + ".json");
13718
+ }
13719
+
13720
+ function safeFilePart(value) {
13721
+ return String(value).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 128);
13722
+ }
13723
+
13724
+ export function enqueueSummaryCapture(queueDir, summary, initiativeId, nowIso, captureKind) {
13725
+ var root = queueDir ? queueDir : defaultQueueDir();
13726
+ var stamp = String(nowIso).replace(/[^A-Za-z0-9]/g, "_");
13727
+ var captureId = [summary.source_client, summary.session_id, summary.ended_at, summary.events].join(":");
13728
+ var nonce = Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 10);
13729
+ var name = "summary-" + safeFilePart(summary.source_client) + "-" + safeFilePart(summary.session_id) + "-" + stamp + "-" + process.pid + "-" + nonce + ".capture.json";
13730
+ var target = join(root, name);
13731
+ var temporary = target + ".tmp";
13732
+ var capture = {
13733
+ schema_version: "orgx-session-summary-capture/v1",
13734
+ capture_id: captureId,
13735
+ capture_kind: captureKind,
13736
+ queued_at: nowIso,
13737
+ session: summary,
13738
+ };
13739
+ if (initiativeId) capture.initiative_id = initiativeId;
13740
+ try {
13741
+ mkdirSync(root, { recursive: true, mode: 0o700 });
13742
+ var descriptor = openSync(temporary, "w", 0o600);
13743
+ try {
13744
+ writeSync(descriptor, JSON.stringify(capture), undefined, "utf8");
13745
+ fsyncSync(descriptor);
13746
+ } finally {
13747
+ closeSync(descriptor);
13748
+ }
13749
+ renameSync(temporary, target);
13750
+ return { path: target, capture: capture };
13751
+ } catch (e) {
13752
+ try { rmSync(temporary, { force: true }); } catch (ignored) {}
13753
+ return null;
13754
+ }
13755
+ }
13756
+
13757
+ export function emptyState(sessionId, sourceClient, cwd, nowIso) {
13758
+ return {
13759
+ session_id: sessionId,
13760
+ source_client: sourceClient,
13761
+ cwd: cwd === undefined ? null : cwd,
13762
+ repo: repoOf(cwd),
13763
+ first_ts: nowIso,
13764
+ last_ts: nowIso,
13765
+ events: 0,
13766
+ prompts: 0,
13767
+ tool_calls: 0,
13768
+ tools: {},
13769
+ edits: 0,
13770
+ bash: 0,
13771
+ action_seq: 0,
13772
+ actions: [],
13773
+ actions_omitted: 0,
13774
+ pending_actions: [],
13775
+ pending_starts_omitted: 0,
13776
+ permission_requests: 0,
13777
+ permission_modes: [],
13778
+ work_context: null,
13779
+ };
13780
+ }
13781
+
13782
+ // One bounded read-modify-write. No spool, no network, no unbounded growth:
13783
+ // the tool map is capped so a pathological session cannot inflate the file.
13784
+ var MAX_TOOL_KEYS = 40;
13785
+ var MAX_CAPTURE_ACTIONS = 32;
13786
+ var MAX_PENDING_ACTIONS = 32;
13787
+
13788
+ function isPromptEvent(event) {
13789
+ return event === "UserPromptSubmit" || String(event).indexOf("user_prompt") >= 0;
13790
+ }
13791
+
13792
+ function isToolCompletionEvent(event) {
13793
+ var normalized = String(event).toLowerCase();
13794
+ return event === "PostToolUse" ||
13795
+ event === "PostToolUseFailure" ||
13796
+ normalized.indexOf("post_tool_use") >= 0;
13797
+ }
13798
+
13799
+ function ensureExecutionState(state) {
13800
+ if (!Number.isFinite(state.action_seq)) state.action_seq = 0;
13801
+ if (!Array.isArray(state.actions)) state.actions = [];
13802
+ if (!Number.isFinite(state.actions_omitted)) state.actions_omitted = 0;
13803
+ if (!Array.isArray(state.pending_actions)) state.pending_actions = [];
13804
+ if (!Number.isFinite(state.pending_starts_omitted)) state.pending_starts_omitted = 0;
13805
+ if (!Number.isFinite(state.permission_requests)) state.permission_requests = 0;
13806
+ if (!Array.isArray(state.permission_modes)) state.permission_modes = [];
13807
+ return state;
13808
+ }
13809
+
13810
+ function nextActionId(state) {
13811
+ state.action_seq += 1;
13812
+ return "action-" + String(state.action_seq).padStart(6, "0");
13813
+ }
13814
+
13815
+ function boundedString(value, max) {
13816
+ var text = pickString(value);
13817
+ return text ? text.slice(0, max) : undefined;
13818
+ }
13819
+
13820
+ function safeDuration(value) {
13821
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return undefined;
13822
+ return Math.min(Math.round(value), 86400000);
13823
+ }
13824
+
13825
+ function actionResult(event, sourceClient) {
13826
+ if (event === "PostToolUseFailure" || String(event).toLowerCase().indexOf("failure") >= 0) {
13827
+ return "failed";
13828
+ }
13829
+ // Claude's PostToolUse contract fires only after success. Codex documents
13830
+ // PostToolUse for completed tools, including non-zero Bash exits, so its
13831
+ // outcome must remain explicitly unclassified without reading tool output.
13832
+ return sourceClient === "claude-code" ? "succeeded" : "completed_unknown";
13833
+ }
13834
+
13835
+ function pendingIndex(state, toolUseId) {
13836
+ if (!toolUseId) return -1;
13837
+ for (var i = 0; i < state.pending_actions.length; i++) {
13838
+ if (state.pending_actions[i].correlation_key === toolUseId) return i;
13839
+ }
13840
+ return -1;
13841
+ }
13842
+
13843
+ function observeAction(state, input) {
13844
+ var event = input.event;
13845
+ var toolName = boundedString(input.toolName, 120);
13846
+ if (!toolName) return;
13847
+ var toolUseId = boundedString(input.toolUseId, 200);
13848
+
13849
+ if (event === "PreToolUse" || String(event).toLowerCase().indexOf("pre_tool_use") >= 0) {
13850
+ if (pendingIndex(state, toolUseId) >= 0) return;
13851
+ if (state.pending_actions.length >= MAX_PENDING_ACTIONS) {
13852
+ state.pending_starts_omitted += 1;
13853
+ return;
13854
+ }
13855
+ state.pending_actions.push({
13856
+ id: nextActionId(state),
13857
+ correlation_key: toolUseId,
13858
+ tool_name: normalizeToolName(toolName),
13859
+ started_at: input.nowIso,
13860
+ turn_id: boundedString(input.turnId, 120),
13861
+ });
13862
+ return;
13863
+ }
13864
+
13865
+ if (!isToolCompletionEvent(event)) return;
13866
+ var index = pendingIndex(state, toolUseId);
13867
+ var pending = index >= 0 ? state.pending_actions.splice(index, 1)[0] : null;
13868
+ if (!pending) state.pending_starts_omitted += 1;
13869
+ var action = {
13870
+ id: pending ? pending.id : nextActionId(state),
13871
+ type: "tool.invoke",
13872
+ tool_name: normalizeToolName(toolName),
13873
+ status: actionResult(event, state.source_client),
13874
+ started_at: pending ? pending.started_at : undefined,
13875
+ completed_at: input.nowIso,
13876
+ duration_ms: safeDuration(input.durationMs),
13877
+ turn_id: boundedString(input.turnId, 120) || (pending ? pending.turn_id : undefined),
13878
+ provenance: "runtime_observed",
13879
+ };
13880
+ if (state.actions.length < MAX_CAPTURE_ACTIONS) state.actions.push(action);
13881
+ else state.actions_omitted += 1;
13882
+ }
13883
+
13884
+ export function applyEvent(state, input) {
13885
+ ensureExecutionState(state);
13886
+ var event = input.event;
13887
+ var toolName = input.toolName;
13888
+ state.events += 1;
13889
+ state.last_ts = input.nowIso;
13890
+
13891
+ if (isPromptEvent(event)) state.prompts += 1;
13892
+
13893
+ if (event === "PermissionRequest") state.permission_requests += 1;
13894
+ var permissionMode = boundedString(input.permissionMode, 40);
13895
+ if (
13896
+ permissionMode &&
13897
+ state.permission_modes.indexOf(permissionMode) < 0 &&
13898
+ state.permission_modes.length < 8
13899
+ ) {
13900
+ state.permission_modes.push(permissionMode);
13901
+ }
13902
+
13903
+ observeAction(state, input);
13904
+
13905
+ if (toolName && isToolCompletionEvent(event)) {
13906
+ state.tool_calls += 1;
13907
+ var known = state.tools[toolName] !== undefined;
13908
+ if (known || Object.keys(state.tools).length < MAX_TOOL_KEYS) {
13909
+ state.tools[toolName] = (state.tools[toolName] || 0) + 1;
13910
+ }
13911
+ if (toolName === "Write" || toolName === "Edit" || toolName === "NotebookEdit") state.edits += 1;
13912
+ if (toolName === "Bash") state.bash += 1;
13913
+ }
13914
+ return state;
13915
+ }
13916
+
13917
+ // Long MCP tool names ("mcp__<uuid>__<tool>") can make a raw tool map dominate
13918
+ // the payload, so keep the heaviest tools and bucket the tail. This bounds the
13919
+ // summary regardless of how varied or long a session gets.
13920
+ var MAX_REPORTED_TOOLS = 12;
13921
+ var MAX_TOOL_NAME = 48;
13922
+
13923
+ // "mcp__c2ed4428-8c64-4586-9c2a-52eac05637b8__search-contacts" is mostly an
13924
+ // opaque server uuid. The tool suffix is the part with analytic value, so
13925
+ // collapse the uuid rather than truncating the informative end.
13926
+ export function normalizeToolName(name) {
13927
+ var mcp = /^mcp__[0-9a-fA-F-]{16,}__(.+)$/.exec(name);
13928
+ var base = mcp ? "mcp__" + mcp[1] : name;
13929
+ return base.length > MAX_TOOL_NAME ? base.slice(0, MAX_TOOL_NAME) : base;
13930
+ }
13931
+
13932
+ export function boundTools(tools) {
13933
+ // Collapse first so two variants of the same tool merge before ranking.
13934
+ var merged = {};
13935
+ var names = Object.keys(tools);
13936
+ for (var i = 0; i < names.length; i++) {
13937
+ var key = normalizeToolName(names[i]);
13938
+ merged[key] = (merged[key] || 0) + tools[names[i]];
13939
+ }
13940
+ var entries = Object.keys(merged).map(function (k) { return [k, merged[k]]; });
13941
+ entries.sort(function (a, b) { return b[1] - a[1]; });
13942
+ var out = {};
13943
+ if (entries.length <= MAX_REPORTED_TOOLS) {
13944
+ for (var j = 0; j < entries.length; j++) out[entries[j][0]] = entries[j][1];
13945
+ return out;
13946
+ }
13947
+ for (var k = 0; k < MAX_REPORTED_TOOLS; k++) out[entries[k][0]] = entries[k][1];
13948
+ var other = 0;
13949
+ for (var m = MAX_REPORTED_TOOLS; m < entries.length; m++) other += entries[m][1];
13950
+ out.__other__ = other;
13951
+ out.__other_tools__ = entries.length - MAX_REPORTED_TOOLS;
13952
+ return out;
13953
+ }
13954
+
13955
+ function buildExecutionObservation(state, captureKind) {
13956
+ ensureExecutionState(state);
13957
+ var remaining = Math.max(0, MAX_CAPTURE_ACTIONS - state.actions.length);
13958
+ var includedPending = state.pending_actions.slice(0, remaining).map(function (pending) {
13959
+ return {
13960
+ id: pending.id,
13961
+ type: "tool.invoke",
13962
+ tool_name: pending.tool_name,
13963
+ status: "running",
13964
+ started_at: pending.started_at,
13965
+ turn_id: pending.turn_id,
13966
+ provenance: "runtime_observed",
13967
+ };
13968
+ });
13969
+ var pendingOmitted = Math.max(0, state.pending_actions.length - includedPending.length);
13970
+ var actions = state.actions.concat(includedPending).map(function (action) {
13971
+ var out = {
13972
+ id: boundedString(action.id, 80),
13973
+ type: "tool.invoke",
13974
+ tool_name: boundedString(action.tool_name, 48),
13975
+ status: action.status,
13976
+ provenance: "runtime_observed",
13977
+ };
13978
+ if (action.started_at) out.started_at = action.started_at;
13979
+ if (action.completed_at) out.completed_at = action.completed_at;
13980
+ if (action.duration_ms !== undefined) out.duration_ms = action.duration_ms;
13981
+ if (action.turn_id) out.turn_id = action.turn_id;
13982
+ return out;
13983
+ });
13984
+ var omitted = state.actions_omitted + pendingOmitted;
13985
+ var workContext = state.work_context && typeof state.work_context === "object"
13986
+ ? state.work_context
13987
+ : null;
13988
+ var unavailable = ["verification"];
13989
+ if (!workContext || !workContext.intent) unavailable.push("intent");
13990
+ if (!workContext || !workContext.authority) unavailable.push("authority_decision");
13991
+ if (!workContext || !workContext.cost) unavailable.push("cost");
13992
+ if (!workContext || !Array.isArray(workContext.artifact_refs) || workContext.artifact_refs.length === 0) {
13993
+ unavailable.push("artifact_refs");
13994
+ }
13995
+ return {
13996
+ schema_version: "orgx-session-execution-observation/v1",
13997
+ boundary: captureKind || "legacy_unspecified",
13998
+ terminal_observed: captureKind === "session_end",
13999
+ actions: actions,
14000
+ actions_completed_observed: state.tool_calls,
14001
+ actions_omitted: omitted,
14002
+ start_times_omitted: state.pending_starts_omitted,
14003
+ pending_actions: state.pending_actions.length,
14004
+ permission_requests_observed: state.permission_requests,
14005
+ permission_modes_observed: state.permission_modes.slice(0, 8),
14006
+ action_capture_complete:
14007
+ omitted === 0 &&
14008
+ state.pending_starts_omitted === 0 &&
14009
+ state.pending_actions.length === 0,
14010
+ unavailable_fields: unavailable,
14011
+ };
14012
+ }
14013
+
14014
+ export function buildSummary(state, nowIso, captureKind) {
14015
+ var first = Date.parse(state.first_ts);
14016
+ var last = Date.parse(state.last_ts || nowIso);
14017
+ var spanOk = isFinite(first) && isFinite(last);
14018
+ var metadata = { execution: buildExecutionObservation(state, captureKind) };
14019
+ if (state.work_context && typeof state.work_context === "object") {
14020
+ metadata.work_context = state.work_context;
14021
+ }
14022
+ return {
14023
+ schema_version: SCHEMA_VERSION,
14024
+ source: "orgx_session_summary",
14025
+ session_id: state.session_id,
14026
+ source_client: state.source_client,
14027
+ repo: state.repo,
14028
+ cwd: state.cwd,
14029
+ day: isFinite(first) ? new Date(first).toISOString().slice(0, 10) : "unknown",
14030
+ started_at: state.first_ts,
14031
+ ended_at: state.last_ts,
14032
+ duration_min: spanOk ? Math.round((last - first) / 60000) : 0,
14033
+ events: state.events,
14034
+ prompts: state.prompts,
14035
+ tool_calls: state.tool_calls,
14036
+ tools: boundTools(state.tools),
14037
+ edits: state.edits,
14038
+ bash: state.bash,
14039
+ metadata: metadata,
14040
+ };
14041
+ }
14042
+
14043
+ export function readState(path) {
14044
+ try {
14045
+ var parsed = JSON.parse(readFileSync(path, "utf8"));
14046
+ var ok = parsed && typeof parsed === "object" && !Array.isArray(parsed);
14047
+ return ok ? parsed : null;
14048
+ } catch (e) {
14049
+ return null;
14050
+ }
14051
+ }
14052
+
14053
+ export function writeState(path, state) {
14054
+ try {
14055
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
14056
+ writeFileSync(path, JSON.stringify(state), { encoding: "utf8", mode: 0o600 });
14057
+ return true;
14058
+ } catch (e) {
14059
+ return false;
14060
+ }
14061
+ }
14062
+
14063
+ async function readStdin() {
14064
+ try {
14065
+ var chunks = [];
14066
+ for await (var chunk of process.stdin) chunks.push(Buffer.from(chunk));
14067
+ return Buffer.concat(chunks).toString("utf8");
14068
+ } catch (e) {
14069
+ return "";
14070
+ }
14071
+ }
14072
+
14073
+ export function parseJson(value) {
14074
+ try {
14075
+ var parsed = JSON.parse(value || "{}");
14076
+ var ok = parsed && typeof parsed === "object" && !Array.isArray(parsed);
14077
+ return ok ? parsed : {};
14078
+ } catch (e) {
14079
+ return {};
14080
+ }
14081
+ }
14082
+
14083
+ function workString(value, max) {
14084
+ if (typeof value !== "string") return null;
14085
+ var trimmed = value.trim();
14086
+ return trimmed && trimmed.length <= max ? trimmed : null;
14087
+ }
14088
+
14089
+ function workObject(value) {
14090
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
14091
+ }
14092
+
14093
+ function workStrings(value, max) {
14094
+ if (value === undefined) return [];
14095
+ if (!Array.isArray(value) || value.length > 20) return null;
14096
+ var output = [];
14097
+ for (var i = 0; i < value.length; i++) {
14098
+ var item = workString(value[i], max);
14099
+ if (!item) return null;
14100
+ if (output.indexOf(item) < 0) output.push(item);
14101
+ }
14102
+ return output;
14103
+ }
14104
+
14105
+ function workRef(value) {
14106
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
14107
+ var system = workString(value.system, 120);
14108
+ var type = workString(value.type, 120);
14109
+ var id = workString(value.id, 500);
14110
+ var uri = value.uri === undefined ? undefined : workString(value.uri, 1000);
14111
+ var version = value.version === undefined ? undefined : workString(value.version, 120);
14112
+ if (!system || !type || !id || uri === null || version === null) return null;
14113
+ if (uri && /[\0- ]/.test(uri)) return null;
14114
+ var ref = { system: system, type: type, id: id };
14115
+ if (uri) ref.uri = uri;
14116
+ if (version) ref.version = version;
14117
+ return ref;
14118
+ }
14119
+
14120
+ function workRefs(value) {
14121
+ if (value === undefined) return [];
14122
+ if (!Array.isArray(value) || value.length > 20) return null;
14123
+ var refs = [];
14124
+ var seen = {};
14125
+ for (var i = 0; i < value.length; i++) {
14126
+ var ref = workRef(value[i]);
14127
+ if (!ref) return null;
14128
+ var key = ref.system + "\0" + ref.type + "\0" + ref.id;
14129
+ if (!seen[key]) {
14130
+ seen[key] = true;
14131
+ refs.push(ref);
14132
+ }
14133
+ }
14134
+ refs.sort(function (a, b) {
14135
+ var left = a.system + "\0" + a.type + "\0" + a.id;
14136
+ var right = b.system + "\0" + b.type + "\0" + b.id;
14137
+ return left < right ? -1 : left > right ? 1 : 0;
14138
+ });
14139
+ return refs;
14140
+ }
14141
+
14142
+ function workIso(value) {
14143
+ var text = workString(value, 100);
14144
+ return text && /^d{4}-d{2}-d{2}Td{2}:d{2}:d{2}(?:.d+)?(?:Z|[+-]d{2}:d{2})$/.test(text) && !Number.isNaN(Date.parse(text))
14145
+ ? text
14146
+ : null;
14147
+ }
14148
+
14149
+ export function parseExplicitWorkContext(value) {
14150
+ if (!value || Buffer.byteLength(String(value), "utf8") > 4096) return null;
14151
+ var parsed = parseJson(value);
14152
+ if (parsed.schema_version !== "orgx-session-work-context/v1") return null;
14153
+ var intent = workObject(parsed.intent);
14154
+ var authority = workObject(parsed.authority);
14155
+ var scope = workObject(authority && authority.scope);
14156
+ var cost = workObject(parsed.cost);
14157
+ if (!intent || !authority || !scope || !cost) return null;
14158
+ var summary = workString(intent.summary, 2000);
14159
+ var objective = intent.objective === undefined ? undefined : workString(intent.objective, 4000);
14160
+ var acceptance = workStrings(intent.acceptance_criteria, 1000);
14161
+ var intentConstraints = workStrings(intent.constraints, 1000);
14162
+ var requestRef = intent.request_ref === undefined ? undefined : workRef(intent.request_ref);
14163
+ var modes = ["explicit", "delegated", "inherited", "policy", "none", "unknown"];
14164
+ var statuses = ["granted", "restricted", "denied", "expired", "unknown"];
14165
+ var mode = workString(authority.mode, 40);
14166
+ var status = workString(authority.status, 40);
14167
+ var actions = workStrings(scope.actions, 120);
14168
+ var resources = workRefs(scope.resources);
14169
+ var systems = workStrings(scope.systems, 120);
14170
+ var spend = scope.spend_limit === undefined ? undefined : workObject(scope.spend_limit);
14171
+ var spendCurrency = spend === undefined ? undefined : workString(spend && spend.currency, 12);
14172
+ var spendAmount = spend && spend.amount;
14173
+ var authorityConstraints = workStrings(authority.constraints, 1000);
14174
+ var authorizationRef = authority.authorization_ref === undefined ? undefined : workRef(authority.authorization_ref);
14175
+ var validFrom = authority.valid_from === undefined ? undefined : workIso(authority.valid_from);
14176
+ var validUntil = authority.valid_until === undefined ? undefined : workIso(authority.valid_until);
14177
+ var artifactRefs = workRefs(parsed.artifact_refs);
14178
+ var evidenceRefs = workRefs(parsed.evidence_refs);
14179
+ var currency = workString(cost.currency, 12);
14180
+ var source = cost.source === undefined ? undefined : workString(cost.source, 120);
14181
+ if (!summary || objective === null || acceptance === null || intentConstraints === null || requestRef === null) return null;
14182
+ if (!mode || modes.indexOf(mode) < 0 || !status || statuses.indexOf(status) < 0) return null;
14183
+ if (actions === null || resources === null || systems === null || authorityConstraints === null || authorizationRef === null) return null;
14184
+ if (spend !== undefined && (!spend || !spendCurrency || !/^[A-Z][A-Z0-9_-]{1,11}$/.test(spendCurrency) || typeof spendAmount !== "number" || !Number.isFinite(spendAmount) || spendAmount < 0 || spendAmount > 100000)) return null;
14185
+ if (validFrom === null || validUntil === null || (validFrom && validUntil && Date.parse(validUntil) < Date.parse(validFrom))) return null;
14186
+ if (!currency || !/^[A-Z][A-Z0-9_-]{1,11}$/.test(currency) || typeof cost.total !== "number" || !Number.isFinite(cost.total) || cost.total < 0 || cost.total > 100000 || source === null) return null;
14187
+ if (cost.estimated !== undefined && typeof cost.estimated !== "boolean") return null;
14188
+ if (artifactRefs === null || evidenceRefs === null) return null;
14189
+ var normalizedIntent = { summary: summary, acceptance_criteria: acceptance, constraints: intentConstraints };
14190
+ if (objective) normalizedIntent.objective = objective;
14191
+ if (requestRef) normalizedIntent.request_ref = requestRef;
14192
+ var normalizedAuthority = {
14193
+ mode: mode,
14194
+ status: status,
14195
+ scope: { actions: actions, resources: resources, systems: systems },
14196
+ constraints: authorityConstraints,
14197
+ };
14198
+ if (spend !== undefined) normalizedAuthority.scope.spend_limit = { currency: spendCurrency, amount: spendAmount };
14199
+ if (authorizationRef) normalizedAuthority.authorization_ref = authorizationRef;
14200
+ if (validFrom) normalizedAuthority.valid_from = validFrom;
14201
+ if (validUntil) normalizedAuthority.valid_until = validUntil;
14202
+ var normalizedCost = { currency: currency, total: cost.total, estimated: cost.estimated !== false };
14203
+ if (source) normalizedCost.source = source;
14204
+ return {
14205
+ schema_version: "orgx-session-work-context/v1",
14206
+ provenance: "producer_asserted",
14207
+ intent: normalizedIntent,
14208
+ authority: normalizedAuthority,
14209
+ cost: normalizedCost,
14210
+ artifact_refs: artifactRefs,
14211
+ evidence_refs: evidenceRefs,
14212
+ };
14213
+ }
14214
+
14215
+ function autoFlushDisabled(value) {
14216
+ var normalized = String(value || "").trim().toLowerCase();
14217
+ return normalized === "off" || normalized === "false" || normalized === "0";
14218
+ }
14219
+
14220
+ /**
14221
+ * Ask a detached Wizard process to deliver the durable queue. The hook itself
14222
+ * performs no network or credential work and never waits for delivery.
14223
+ */
14224
+ export function triggerQueueDelivery(args, env, queueDir, spawnImpl) {
14225
+ if (autoFlushDisabled(env.ORGX_SESSION_SUMMARY_AUTO_FLUSH)) return false;
14226
+ var nodePath = pickString(args.delivery_node);
14227
+ var cliPath = pickString(args.delivery_cli);
14228
+ if (!nodePath || !cliPath || !queueDir) return false;
14229
+ var configuredLimit = parseInt(
14230
+ pickString(args.auto_flush_limit, env.ORGX_SESSION_SUMMARY_AUTO_FLUSH_LIMIT) || "",
14231
+ 10
14232
+ );
14233
+ var limit = Number.isFinite(configuredLimit)
14234
+ ? Math.max(1, Math.min(configuredLimit, 100))
14235
+ : 25;
14236
+ try {
14237
+ var child = spawnImpl(
14238
+ nodePath,
14239
+ [
14240
+ cliPath,
14241
+ "hooks",
14242
+ "flush",
14243
+ "--background",
14244
+ "--limit=" + String(limit),
14245
+ "--queue=" + queueDir,
14246
+ ],
14247
+ { detached: true, stdio: "ignore" }
14248
+ );
14249
+ if (child && typeof child.on === "function") {
14250
+ child.on("error", function () {});
14251
+ }
14252
+ if (child && typeof child.unref === "function") child.unref();
14253
+ return true;
14254
+ } catch (e) {
14255
+ return false;
14256
+ }
14257
+ }
14258
+
14259
+ export async function main(options) {
14260
+ var opts = options || {};
14261
+ var argv = opts.argv ? opts.argv : process.argv.slice(2);
14262
+ var env = opts.env ? opts.env : process.env;
14263
+ var stdinText = opts.stdinText === undefined ? "" : opts.stdinText;
14264
+ var now = opts.now ? opts.now : function () { return new Date().toISOString(); };
14265
+ var dir = opts.dir;
14266
+ var spawnImpl = opts.spawnImpl ? opts.spawnImpl : spawn;
14267
+
14268
+ var args = parseArgs(argv);
14269
+ var payload = parseJson(stdinText);
14270
+ var nowIso = now();
14271
+ var explicitWorkContext = parseExplicitWorkContext(env.ORGX_SESSION_WORK_CONTEXT);
14272
+
14273
+ var event = pickString(args.event, payload.hook_event_name, payload.hookEventName, "unknown");
14274
+ var sessionId = pickString(payload.session_id, payload.sessionId, args.session_id);
14275
+ if (!sessionId) return { ok: true, skipped: "missing_session_id" };
14276
+
14277
+ var sourceClient = pickString(args.source_client, env.ORGX_SOURCE_CLIENT, "claude-code");
14278
+ var cwd = pickString(payload.cwd, args.cwd);
14279
+ var stateDir = dir ? dir : pickString(args.state_dir, env.ORGX_SESSION_STATE_DIR);
14280
+ var path = statePath(sessionId, stateDir);
14281
+
14282
+ var terminal = event === "Stop" || event === "SessionEnd";
14283
+
14284
+ if (!terminal) {
14285
+ // Hot path: one small file read + write. Never a network call.
14286
+ var existing = readState(path);
14287
+ var state = existing ? existing : emptyState(sessionId, sourceClient, cwd, nowIso);
14288
+ if (!state.cwd && cwd) {
14289
+ state.cwd = cwd;
14290
+ state.repo = repoOf(cwd);
14291
+ }
14292
+ if (explicitWorkContext) state.work_context = explicitWorkContext;
14293
+ var toolName = pickString(
14294
+ payload.tool_name,
14295
+ payload.toolName,
14296
+ payload.tool ? payload.tool.name : undefined
14297
+ );
14298
+ applyEvent(state, {
14299
+ event: event,
14300
+ toolName: toolName,
14301
+ toolUseId: pickString(payload.tool_use_id, payload.toolUseId),
14302
+ turnId: pickString(payload.turn_id, payload.turnId),
14303
+ durationMs: payload.duration_ms,
14304
+ permissionMode: pickString(payload.permission_mode, payload.permissionMode),
14305
+ nowIso: nowIso,
14306
+ });
14307
+ writeState(path, state);
14308
+ return { ok: true, counted: true, event: event };
14309
+ }
14310
+
14311
+ // Terminal: persist one cumulative snapshot, then ask a detached Wizard
14312
+ // worker to flush. The hook never owns credentials, retries, or network I/O.
14313
+ var finalState = readState(path);
14314
+ if (!finalState) return { ok: true, skipped: "no_session_state" };
14315
+ if (explicitWorkContext) finalState.work_context = explicitWorkContext;
14316
+ var initiativeId = pickString(env.ORGX_INITIATIVE_ID, args.initiative);
14317
+ var queueDir = pickString(args.queue_dir, env.ORGX_SESSION_SUMMARY_QUEUE_DIR) || defaultQueueDir();
14318
+ var captureKind = event === "SessionEnd" ? "session_end" : "turn_boundary";
14319
+ var summary = buildSummary(finalState, nowIso, captureKind);
14320
+ var queued = enqueueSummaryCapture(queueDir, summary, initiativeId, nowIso, captureKind);
14321
+ if (!queued) return { ok: true, skipped: "queue_write_failed", summary: summary };
14322
+ var deliveryTriggered = triggerQueueDelivery(args, env, queueDir, spawnImpl);
14323
+
14324
+ if (event === "SessionEnd") {
14325
+ try {
14326
+ rmSync(path, { force: true });
14327
+ } catch (e) {
14328
+ // keep going
14329
+ }
14330
+ }
14331
+
14332
+ return {
14333
+ ok: true,
14334
+ queued: true,
14335
+ queue_path: queued.path,
14336
+ delivery_triggered: deliveryTriggered,
14337
+ final: event === "SessionEnd",
14338
+ summary: summary,
14339
+ };
14340
+ }
14341
+
14342
+ var invokedDirectly = process.argv[1] && import.meta.url === "file://" + process.argv[1];
14343
+ if (invokedDirectly) {
14344
+ readStdin()
14345
+ .then(function (text) { return main({ stdinText: text }); })
14346
+ .catch(function () {})
14347
+ .finally(function () { process.exit(0); });
14348
+ }
14349
+ `;
14350
+ }
14351
+
14352
+ // src/lib/runtime-hooks.ts
13629
14353
  var HOOK_MARKER = "orgx-session-hook.mjs";
13630
14354
  var EMIT_HOOK_MARKER = "orgx-emit-execution-graph.mjs";
13631
- var HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PermissionRequest", "Stop"];
13632
- var CLAUDE_HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "SubagentStop", "Stop", "SessionEnd"];
14355
+ var SUMMARY_HOOK_MARKER = SESSION_SUMMARY_HOOK_MARKER;
14356
+ var HOOK_EVENTS = [
14357
+ "SessionStart",
14358
+ "UserPromptSubmit",
14359
+ "PreToolUse",
14360
+ "PostToolUse",
14361
+ "PermissionRequest",
14362
+ "Stop",
14363
+ "SessionEnd"
14364
+ ];
14365
+ var CLAUDE_HOOK_EVENTS = [
14366
+ "SessionStart",
14367
+ "UserPromptSubmit",
14368
+ "PreToolUse",
14369
+ "PostToolUse",
14370
+ "PostToolUseFailure",
14371
+ "PermissionRequest",
14372
+ "SubagentStop",
14373
+ "Stop",
14374
+ "SessionEnd"
14375
+ ];
14376
+ function currentPackagedCliPath() {
14377
+ if (!process.argv[1]) return "";
14378
+ const candidate = resolve2(process.argv[1]);
14379
+ return /\.(?:cts|mts|ts|tsx)$/i.test(candidate) ? "" : candidate;
14380
+ }
13633
14381
  function defaultPaths(options = {}) {
13634
14382
  const hookDir = join8(ORGX_WIZARD_CONFIG_HOME, "hooks");
13635
14383
  return {
@@ -13638,6 +14386,11 @@ function defaultPaths(options = {}) {
13638
14386
  codexHooksPath: options.codexHooksPath ?? join8(CODEX_DIR, "hooks.json"),
13639
14387
  hookScriptPath: options.hookScriptPath ?? join8(hookDir, HOOK_MARKER),
13640
14388
  emitHookScriptPath: options.emitHookScriptPath ?? join8(hookDir, EMIT_HOOK_MARKER),
14389
+ summaryHookScriptPath: options.summaryHookScriptPath ?? join8(hookDir, SUMMARY_HOOK_MARKER),
14390
+ sessionStateDir: options.sessionStateDir ?? join8(ORGX_WIZARD_CONFIG_HOME, "sessions"),
14391
+ sessionSummaryQueueDir: options.sessionSummaryQueueDir ?? join8(ORGX_WIZARD_CONFIG_HOME, "session-summary-captures"),
14392
+ deliveryNodePath: options.deliveryNodePath ?? process.execPath,
14393
+ deliveryCliPath: options.deliveryCliPath ?? currentPackagedCliPath(),
13641
14394
  outboxPath: options.outboxPath ?? join8(hookDir, "events.jsonl")
13642
14395
  };
13643
14396
  }
@@ -13646,6 +14399,13 @@ function countJsonlLines(path) {
13646
14399
  if (!raw) return 0;
13647
14400
  return raw.split(/\r?\n/).filter((line) => line.trim().length > 0).length;
13648
14401
  }
14402
+ function countSessionSummaryCaptures(path) {
14403
+ try {
14404
+ return readdirSync6(path).filter((name) => name.endsWith(".capture.json")).length;
14405
+ } catch {
14406
+ return 0;
14407
+ }
14408
+ }
13649
14409
  function backupPath(path, now) {
13650
14410
  const timestamp = now.toISOString().replace(/[:.]/g, "-");
13651
14411
  return `${path}.bak.${timestamp}`;
@@ -13667,10 +14427,29 @@ function codexHasNotify(raw) {
13667
14427
  }
13668
14428
  function buildRuntimeHookScriptContent() {
13669
14429
  return `#!/usr/bin/env node
13670
- import { appendFileSync, mkdirSync } from "node:fs";
14430
+ import { appendFileSync, mkdirSync, renameSync, statSync } from "node:fs";
13671
14431
  import { dirname, join } from "node:path";
13672
14432
  import { homedir } from "node:os";
13673
14433
 
14434
+ const MAX_OUTBOX_BYTES = ${HOOK_OUTBOX_MAX_BYTES};
14435
+
14436
+ function outboxLimit() {
14437
+ const raw = parseInt(process.env.ORGX_WIZARD_HOOK_OUTBOX_MAX_BYTES || "", 10);
14438
+ return Number.isFinite(raw) && raw > 0 ? raw : MAX_OUTBOX_BYTES;
14439
+ }
14440
+
14441
+ function spoolDisabled() {
14442
+ return String(process.env.ORGX_WIZARD_HOOK_SPOOL || "").trim().toLowerCase() === "off";
14443
+ }
14444
+
14445
+ function rotateIfOversized(path) {
14446
+ try {
14447
+ if (statSync(path).size > outboxLimit()) renameSync(path, path + ".1");
14448
+ } catch (error) {
14449
+ // No spool yet, or rotation is not possible. Either way, keep going.
14450
+ }
14451
+ }
14452
+
13674
14453
  function parseArgs(argv) {
13675
14454
  const args = {};
13676
14455
  for (const arg of argv) {
@@ -13740,8 +14519,11 @@ const record = {
13740
14519
  };
13741
14520
 
13742
14521
  try {
13743
- mkdirSync(dirname(outbox), { recursive: true, mode: 0o700 });
13744
- appendFileSync(outbox, JSON.stringify(record) + "\\n", { encoding: "utf8", mode: 0o600 });
14522
+ if (!spoolDisabled()) {
14523
+ mkdirSync(dirname(outbox), { recursive: true, mode: 0o700 });
14524
+ rotateIfOversized(outbox);
14525
+ appendFileSync(outbox, JSON.stringify(record) + "\\n", { encoding: "utf8", mode: 0o600 });
14526
+ }
13745
14527
  } catch {
13746
14528
  // Hooks must never break the user's agent runtime.
13747
14529
  }
@@ -13840,7 +14622,7 @@ function authHeaders(env) {
13840
14622
  try {
13841
14623
  const args = parseArgs(process.argv.slice(2));
13842
14624
  const env = process.env;
13843
- if (!(truthy(args.enabled) || truthy(env.ORGX_EMIT_EXECUTION_GRAPH))) return;
14625
+ if (!truthy(env.ORGX_EMIT_EXECUTION_GRAPH)) return;
13844
14626
  const initiative = pick(env.ORGX_INITIATIVE_ID, args.initiative);
13845
14627
  if (!initiative) return;
13846
14628
  const auth = authHeaders(env);
@@ -13872,7 +14654,7 @@ function authHeaders(env) {
13872
14654
  const ctrl = new AbortController();
13873
14655
  const timer = setTimeout(() => ctrl.abort(), parseInt(env.ORGX_EMIT_TIMEOUT_MS || "", 10) || 4000);
13874
14656
  try {
13875
- await fetch(base + "/api/client/live/execution-graph", {
14657
+ await fetch(base + "/api/v1/live/execution-graph", {
13876
14658
  method: "POST",
13877
14659
  headers: Object.assign({ "Content-Type": "application/json" }, auth),
13878
14660
  body: JSON.stringify(event),
@@ -13888,10 +14670,69 @@ function buildEmitHookCommand(params) {
13888
14670
  return [
13889
14671
  "node",
13890
14672
  JSON.stringify(params.emitHookScriptPath),
13891
- "--enabled=true",
13892
14673
  `--source_client=${params.sourceClient}`
13893
14674
  ].join(" ");
13894
14675
  }
14676
+ function buildSummaryHookCommand(params) {
14677
+ const terminal = params.event === "Stop" || params.event === "SessionEnd";
14678
+ return [
14679
+ "node",
14680
+ JSON.stringify(params.summaryHookScriptPath),
14681
+ `--event=${params.event}`,
14682
+ `--source_client=${params.sourceClient}`,
14683
+ `--state_dir=${JSON.stringify(params.sessionStateDir)}`,
14684
+ `--queue_dir=${JSON.stringify(params.sessionSummaryQueueDir)}`,
14685
+ ...terminal && params.deliveryNodePath && params.deliveryCliPath ? [
14686
+ `--delivery_node=${JSON.stringify(params.deliveryNodePath)}`,
14687
+ `--delivery_cli=${JSON.stringify(params.deliveryCliPath)}`
14688
+ ] : []
14689
+ ].join(" ");
14690
+ }
14691
+ function ensureClaudeSummaryHook(rules, event, paths) {
14692
+ let changed = false;
14693
+ let universal = rules.find((entry) => isRecord(entry) && entry.matcher === "");
14694
+ if (!universal) {
14695
+ universal = { matcher: "", hooks: [] };
14696
+ rules.push(universal);
14697
+ changed = true;
14698
+ }
14699
+ const desiredCommand = buildSummaryHookCommand({
14700
+ deliveryCliPath: paths.deliveryCliPath,
14701
+ deliveryNodePath: paths.deliveryNodePath,
14702
+ event,
14703
+ sessionStateDir: paths.sessionStateDir,
14704
+ sessionSummaryQueueDir: paths.sessionSummaryQueueDir,
14705
+ sourceClient: "claude-code",
14706
+ summaryHookScriptPath: paths.summaryHookScriptPath
14707
+ });
14708
+ let desiredKept = false;
14709
+ for (const rule of rules) {
14710
+ if (!isRecord(rule)) continue;
14711
+ const hooks = Array.isArray(rule.hooks) ? rule.hooks : [];
14712
+ const next = [];
14713
+ for (const hook of hooks) {
14714
+ const isSummary = isRecord(hook) && typeof hook.command === "string" && hook.command.includes(SUMMARY_HOOK_MARKER);
14715
+ if (!isSummary) {
14716
+ next.push(hook);
14717
+ continue;
14718
+ }
14719
+ if (rule === universal && hook.type === "command" && hook.command === desiredCommand && !desiredKept) {
14720
+ next.push(hook);
14721
+ desiredKept = true;
14722
+ } else {
14723
+ changed = true;
14724
+ }
14725
+ }
14726
+ if (next.length !== hooks.length) rule.hooks = next;
14727
+ }
14728
+ if (!desiredKept) {
14729
+ const hooks = Array.isArray(universal.hooks) ? universal.hooks : [];
14730
+ hooks.push({ type: "command", command: desiredCommand });
14731
+ universal.hooks = hooks;
14732
+ changed = true;
14733
+ }
14734
+ return changed;
14735
+ }
13895
14736
  function mergeCodexHooks(raw, paths) {
13896
14737
  const value = parseJsonObject(raw);
13897
14738
  const hooks = isRecord(value.hooks) ? value.hooks : {};
@@ -13907,10 +14748,31 @@ function mergeCodexHooks(raw, paths) {
13907
14748
  const already = existing.some(
13908
14749
  (entry) => isRecord(entry) && typeof entry.command === "string" && entry.command.includes(HOOK_MARKER)
13909
14750
  );
13910
- if (!already) {
13911
- hooks[event] = [...existing, { command }];
14751
+ const next = already ? [...existing] : [...existing, { command }];
14752
+ if (!already) changed = true;
14753
+ const desiredSummaryCommand = buildSummaryHookCommand({
14754
+ deliveryCliPath: paths.deliveryCliPath,
14755
+ deliveryNodePath: paths.deliveryNodePath,
14756
+ event,
14757
+ sessionStateDir: paths.sessionStateDir,
14758
+ sessionSummaryQueueDir: paths.sessionSummaryQueueDir,
14759
+ sourceClient: "codex",
14760
+ summaryHookScriptPath: paths.summaryHookScriptPath
14761
+ });
14762
+ const summaryEntries = next.filter(
14763
+ (entry) => isRecord(entry) && typeof entry.command === "string" && entry.command.includes(SUMMARY_HOOK_MARKER)
14764
+ );
14765
+ const summaryCurrent = summaryEntries.length === 1 && isRecord(summaryEntries[0]) && summaryEntries[0].command === desiredSummaryCommand;
14766
+ if (!summaryCurrent) {
14767
+ const withoutStaleSummary = next.filter(
14768
+ (entry) => !(isRecord(entry) && typeof entry.command === "string" && entry.command.includes(SUMMARY_HOOK_MARKER))
14769
+ );
14770
+ withoutStaleSummary.push({ command: desiredSummaryCommand });
14771
+ hooks[event] = withoutStaleSummary;
13912
14772
  changed = true;
14773
+ continue;
13913
14774
  }
14775
+ hooks[event] = next;
13914
14776
  }
13915
14777
  value.hooks = hooks;
13916
14778
  return { changed: changed || !raw, value };
@@ -13921,7 +14783,7 @@ function mergeClaudeHooks(raw, paths) {
13921
14783
  let changed = false;
13922
14784
  for (const event of CLAUDE_HOOK_EVENTS) {
13923
14785
  const list = Array.isArray(hooksRoot[event]) ? hooksRoot[event] : [];
13924
- const matcher = event === "PreToolUse" || event === "PostToolUse" ? "Bash|Write|Edit|MultiEdit|mcp__.*" : "";
14786
+ const matcher = event === "PreToolUse" || event === "PostToolUse" || event === "PostToolUseFailure" ? "Bash|Write|Edit|MultiEdit|mcp__.*" : "";
13925
14787
  const command = buildHookCommand({
13926
14788
  event,
13927
14789
  hookScriptPath: paths.hookScriptPath,
@@ -13934,7 +14796,7 @@ function mergeClaudeHooks(raw, paths) {
13934
14796
  list.push(rule);
13935
14797
  changed = true;
13936
14798
  }
13937
- const hooks = Array.isArray(rule.hooks) ? rule.hooks : [];
14799
+ let hooks = Array.isArray(rule.hooks) ? rule.hooks : [];
13938
14800
  const already = hooks.some(
13939
14801
  (entry) => isRecord(entry) && entry.type === "command" && typeof entry.command === "string" && entry.command.includes(HOOK_MARKER)
13940
14802
  );
@@ -13943,6 +14805,10 @@ function mergeClaudeHooks(raw, paths) {
13943
14805
  rule.hooks = hooks;
13944
14806
  changed = true;
13945
14807
  }
14808
+ if (ensureClaudeSummaryHook(list, event, paths)) {
14809
+ changed = true;
14810
+ }
14811
+ hooks = Array.isArray(rule.hooks) ? rule.hooks : [];
13946
14812
  if (event === "Stop") {
13947
14813
  const emitCommand = buildEmitHookCommand({
13948
14814
  emitHookScriptPath: paths.emitHookScriptPath,
@@ -13992,13 +14858,18 @@ function inspectRuntimeHooks(options = {}) {
13992
14858
  const codexConfigRaw = readTextIfExists(paths.codexConfigPath);
13993
14859
  const codexHooksRaw = readTextIfExists(paths.codexHooksPath);
13994
14860
  const claudeSettingsRaw = readTextIfExists(paths.claudeSettingsPath);
14861
+ const hasAutomaticDelivery = (raw) => Boolean(
14862
+ raw && raw.includes("--delivery_node=") && raw.includes("--delivery_cli=") && raw.includes(paths.deliveryNodePath) && raw.includes(paths.deliveryCliPath) && existsSync9(paths.deliveryNodePath) && existsSync9(paths.deliveryCliPath)
14863
+ );
13995
14864
  return {
13996
14865
  paths,
13997
14866
  installed: {
13998
14867
  claudeCode: hasOrgxHook(claudeSettingsRaw),
13999
14868
  codex: hasOrgxHook(codexHooksRaw),
14000
14869
  hookScript: existsSync9(paths.hookScriptPath),
14001
- emitHookScript: existsSync9(paths.emitHookScriptPath)
14870
+ emitHookScript: existsSync9(paths.emitHookScriptPath),
14871
+ summaryHookScript: existsSync9(paths.summaryHookScriptPath),
14872
+ automaticDelivery: hasAutomaticDelivery(codexHooksRaw) || hasAutomaticDelivery(claudeSettingsRaw)
14002
14873
  },
14003
14874
  codex: {
14004
14875
  configExists: Boolean(codexConfigRaw),
@@ -14006,7 +14877,8 @@ function inspectRuntimeHooks(options = {}) {
14006
14877
  hasNotify: codexHasNotify(codexConfigRaw),
14007
14878
  notifyPreserved: !codexHasNotify(codexConfigRaw) || !hasOrgxHook(codexConfigRaw)
14008
14879
  },
14009
- outboxEvents: countJsonlLines(paths.outboxPath)
14880
+ outboxEvents: countJsonlLines(paths.outboxPath),
14881
+ sessionSummaryCaptures: countSessionSummaryCaptures(paths.sessionSummaryQueueDir)
14010
14882
  };
14011
14883
  }
14012
14884
  function installRuntimeHooks(targets, options = {}) {
@@ -14018,7 +14890,8 @@ function installRuntimeHooks(targets, options = {}) {
14018
14890
  codex: false,
14019
14891
  codexConfig: false,
14020
14892
  hookScript: false,
14021
- emitHookScript: false
14893
+ emitHookScript: false,
14894
+ summaryHookScript: false
14022
14895
  };
14023
14896
  mkdirSync4(dirname5(paths.hookScriptPath), { recursive: true, mode: 448 });
14024
14897
  const scriptContent = buildRuntimeHookScriptContent();
@@ -14036,6 +14909,16 @@ function installRuntimeHooks(targets, options = {}) {
14036
14909
  writeTextFile(paths.emitHookScriptPath, emitScriptContent, { mode: 448 });
14037
14910
  changed.emitHookScript = true;
14038
14911
  }
14912
+ mkdirSync4(dirname5(paths.summaryHookScriptPath), { recursive: true, mode: 448 });
14913
+ const summaryScriptContent = buildSessionSummaryHookScriptContent();
14914
+ if (readTextIfExists(paths.summaryHookScriptPath) !== summaryScriptContent) {
14915
+ const backup = backupExisting(paths.summaryHookScriptPath, now);
14916
+ if (backup) backups.push(backup);
14917
+ writeTextFile(paths.summaryHookScriptPath, summaryScriptContent, { mode: 448 });
14918
+ changed.summaryHookScript = true;
14919
+ }
14920
+ mkdirSync4(paths.sessionStateDir, { recursive: true, mode: 448 });
14921
+ mkdirSync4(paths.sessionSummaryQueueDir, { recursive: true, mode: 448 });
14039
14922
  if (targets.includes("codex")) {
14040
14923
  const rawConfig = readTextIfExists(paths.codexConfigPath);
14041
14924
  const nextConfig = ensureCodexHooksFeature(rawConfig);
@@ -14085,6 +14968,679 @@ function parseRuntimeHookTargets(value) {
14085
14968
  return [...new Set(normalized)];
14086
14969
  }
14087
14970
 
14971
+ // src/lib/session-summary-queue.ts
14972
+ import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync8, unlinkSync as unlinkSync2 } from "fs";
14973
+ import { join as join9 } from "path";
14974
+
14975
+ // src/lib/session-summary-backfill.ts
14976
+ import { createReadStream, renameSync as renameSync2, statSync as statSync5, writeFileSync as writeFileSync4 } from "fs";
14977
+ import { basename as basename5 } from "path";
14978
+ import { createInterface } from "readline";
14979
+
14980
+ // src/lib/session-work-context.ts
14981
+ import { z } from "zod";
14982
+ var SESSION_WORK_CONTEXT_VERSION = "orgx-session-work-context/v1";
14983
+ var SESSION_WORK_CONTEXT_MAX_JSON_BYTES = 4 * 1024;
14984
+ var boundedText = (max) => z.string().trim().min(1).max(max);
14985
+ var currency = boundedText(12).regex(/^[A-Z][A-Z0-9_-]{1,11}$/);
14986
+ var externalReferenceSchema = z.object({
14987
+ system: boundedText(120),
14988
+ type: boundedText(120),
14989
+ id: boundedText(500),
14990
+ uri: boundedText(1e3).refine((value) => !/[\u0000-\u0020]/.test(value)).optional(),
14991
+ version: boundedText(120).optional()
14992
+ });
14993
+ var stringList = (max) => z.array(boundedText(max)).max(20).default([]).transform((values) => [
14994
+ ...new Set(values)
14995
+ ]);
14996
+ var referenceList = z.array(externalReferenceSchema).max(20).default([]).transform((refs) => {
14997
+ const unique = /* @__PURE__ */ new Map();
14998
+ for (const ref of refs) {
14999
+ unique.set(`${ref.system}\0${ref.type}\0${ref.id}`, ref);
15000
+ }
15001
+ return [...unique.values()].sort((left, right) => {
15002
+ const a = `${left.system}\0${left.type}\0${left.id}`;
15003
+ const b = `${right.system}\0${right.type}\0${right.id}`;
15004
+ return a < b ? -1 : a > b ? 1 : 0;
15005
+ });
15006
+ });
15007
+ var authoritySchema = z.object({
15008
+ mode: z.enum([
15009
+ "explicit",
15010
+ "delegated",
15011
+ "inherited",
15012
+ "policy",
15013
+ "none",
15014
+ "unknown"
15015
+ ]),
15016
+ status: z.enum(["granted", "restricted", "denied", "expired", "unknown"]),
15017
+ scope: z.object({
15018
+ actions: stringList(120),
15019
+ resources: referenceList,
15020
+ systems: stringList(120),
15021
+ spend_limit: z.object({
15022
+ currency,
15023
+ amount: z.number().finite().min(0).max(1e5)
15024
+ }).optional()
15025
+ }),
15026
+ authorization_ref: externalReferenceSchema.optional(),
15027
+ constraints: stringList(1e3),
15028
+ valid_from: z.string().datetime({ offset: true }).optional(),
15029
+ valid_until: z.string().datetime({ offset: true }).optional()
15030
+ }).refine(
15031
+ (value) => !value.valid_from || !value.valid_until || Date.parse(value.valid_until) >= Date.parse(value.valid_from),
15032
+ { message: "valid_until must not precede valid_from" }
15033
+ );
15034
+ var sessionWorkContextInputSchema = z.object({
15035
+ schema_version: z.literal(SESSION_WORK_CONTEXT_VERSION),
15036
+ intent: z.object({
15037
+ summary: boundedText(2e3),
15038
+ objective: boundedText(4e3).optional(),
15039
+ acceptance_criteria: stringList(1e3),
15040
+ constraints: stringList(1e3),
15041
+ request_ref: externalReferenceSchema.optional()
15042
+ }),
15043
+ authority: authoritySchema,
15044
+ cost: z.object({
15045
+ currency,
15046
+ total: z.number().finite().min(0).max(1e5),
15047
+ estimated: z.boolean().default(true),
15048
+ source: boundedText(120).optional()
15049
+ }),
15050
+ artifact_refs: referenceList,
15051
+ evidence_refs: referenceList
15052
+ });
15053
+ function normalizeSessionWorkContext(value) {
15054
+ const parsed = sessionWorkContextInputSchema.safeParse(value);
15055
+ return parsed.success ? { ...parsed.data, provenance: "producer_asserted" } : null;
15056
+ }
15057
+
15058
+ // src/lib/session-summary-backfill.ts
15059
+ var SESSION_EXECUTION_OBSERVATION_VERSION = "orgx-session-execution-observation/v1";
15060
+ var SESSION_EXECUTION_MAX_ACTIONS = 32;
15061
+ var SESSION_EXECUTION_UNAVAILABLE_FIELDS = /* @__PURE__ */ new Set([
15062
+ "intent",
15063
+ "authority_decision",
15064
+ "artifact_refs",
15065
+ "cost",
15066
+ "verification"
15067
+ ]);
15068
+ var SESSION_EXECUTION_PERMISSION_MODES = /* @__PURE__ */ new Set([
15069
+ "default",
15070
+ "acceptEdits",
15071
+ "plan",
15072
+ "dontAsk",
15073
+ "bypassPermissions"
15074
+ ]);
15075
+ function isRecord3(value) {
15076
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
15077
+ }
15078
+ function boundedString(value, maxLength) {
15079
+ if (typeof value !== "string") return void 0;
15080
+ const trimmed = value.trim();
15081
+ return trimmed ? trimmed.slice(0, maxLength) : void 0;
15082
+ }
15083
+ function boundedCount(value) {
15084
+ return typeof value === "number" && Number.isInteger(value) && value >= 0 ? Math.min(value, 1e7) : 0;
15085
+ }
15086
+ function validTimestamp(value) {
15087
+ const text2 = boundedString(value, 100);
15088
+ return text2 && !Number.isNaN(Date.parse(text2)) ? text2 : void 0;
15089
+ }
15090
+ function normalizeObservedAction(value) {
15091
+ if (!isRecord3(value)) return null;
15092
+ const id = boundedString(value.id, 80);
15093
+ const toolName = boundedString(value.tool_name, 48);
15094
+ const status = boundedString(value.status, 40);
15095
+ if (!id || !toolName || !status || !["running", "succeeded", "failed", "completed_unknown"].includes(status)) {
15096
+ return null;
15097
+ }
15098
+ const startedAt = validTimestamp(value.started_at);
15099
+ const completedAt = validTimestamp(value.completed_at);
15100
+ const durationMs = typeof value.duration_ms === "number" && Number.isInteger(value.duration_ms) && value.duration_ms >= 0 ? Math.min(value.duration_ms, 864e5) : void 0;
15101
+ const turnId = boundedString(value.turn_id, 120);
15102
+ return {
15103
+ id,
15104
+ type: "tool.invoke",
15105
+ tool_name: toolName,
15106
+ status,
15107
+ provenance: "runtime_observed",
15108
+ ...startedAt ? { started_at: startedAt } : {},
15109
+ ...completedAt ? { completed_at: completedAt } : {},
15110
+ ...durationMs !== void 0 ? { duration_ms: durationMs } : {},
15111
+ ...turnId ? { turn_id: turnId } : {}
15112
+ };
15113
+ }
15114
+ function normalizeSessionExecutionObservation(value) {
15115
+ if (!isRecord3(value) || value.schema_version !== SESSION_EXECUTION_OBSERVATION_VERSION) {
15116
+ return null;
15117
+ }
15118
+ const boundary = boundedString(value.boundary, 40);
15119
+ if (!boundary || ![
15120
+ "turn_boundary",
15121
+ "session_end",
15122
+ "historical_backfill",
15123
+ "legacy_unspecified"
15124
+ ].includes(boundary)) {
15125
+ return null;
15126
+ }
15127
+ const actions = (Array.isArray(value.actions) ? value.actions : []).slice(0, SESSION_EXECUTION_MAX_ACTIONS).map(normalizeObservedAction).filter((action) => action !== null);
15128
+ const unavailableFields = (Array.isArray(value.unavailable_fields) ? value.unavailable_fields : []).filter(
15129
+ (field) => typeof field === "string" && SESSION_EXECUTION_UNAVAILABLE_FIELDS.has(field)
15130
+ );
15131
+ const permissionModes = (Array.isArray(value.permission_modes_observed) ? value.permission_modes_observed : []).filter(
15132
+ (mode) => typeof mode === "string" && SESSION_EXECUTION_PERMISSION_MODES.has(mode)
15133
+ );
15134
+ return {
15135
+ schema_version: SESSION_EXECUTION_OBSERVATION_VERSION,
15136
+ boundary,
15137
+ terminal_observed: value.terminal_observed === true,
15138
+ actions,
15139
+ actions_completed_observed: boundedCount(value.actions_completed_observed),
15140
+ actions_omitted: boundedCount(value.actions_omitted),
15141
+ start_times_omitted: boundedCount(value.start_times_omitted),
15142
+ pending_actions: boundedCount(value.pending_actions),
15143
+ permission_requests_observed: boundedCount(value.permission_requests_observed),
15144
+ permission_modes_observed: [...new Set(permissionModes)].slice(0, 8),
15145
+ action_capture_complete: value.action_capture_complete === true,
15146
+ unavailable_fields: [...new Set(unavailableFields)]
15147
+ };
15148
+ }
15149
+ function buildMinimalCloudSummary(summary, metadata) {
15150
+ const execution = normalizeSessionExecutionObservation(summary.metadata?.execution);
15151
+ const workContext = normalizeSessionWorkContext(summary.metadata?.work_context);
15152
+ const { cwd: _localOnlyCwd, metadata: _localOnlyMetadata, ...minimal } = summary;
15153
+ return {
15154
+ ...minimal,
15155
+ metadata: {
15156
+ capture: {
15157
+ ...metadata.captureId ? { id: metadata.captureId } : {},
15158
+ kind: metadata.captureKind,
15159
+ ...metadata.queuedAt ? { queued_at: metadata.queuedAt } : {}
15160
+ },
15161
+ privacy: {
15162
+ profile: "minimal",
15163
+ raw_content_included: false,
15164
+ cwd_included: false
15165
+ },
15166
+ ...execution ? { execution } : {},
15167
+ ...workContext ? { work_context: workContext } : {}
15168
+ }
15169
+ };
15170
+ }
15171
+ var MAX_REPORTED_TOOLS = 12;
15172
+ var MAX_TOOL_NAME = 48;
15173
+ function repoOf(cwd) {
15174
+ if (!cwd) return "unknown";
15175
+ const marker = "/Code/";
15176
+ const at = cwd.indexOf(marker);
15177
+ if (at >= 0) {
15178
+ const rest = cwd.slice(at + marker.length);
15179
+ const slash = rest.indexOf("/");
15180
+ const name = slash < 0 ? rest : rest.slice(0, slash);
15181
+ if (name) return name;
15182
+ }
15183
+ return basename5(cwd) || "unknown";
15184
+ }
15185
+ function normalizeToolName(name) {
15186
+ const mcp = /^mcp__[0-9a-fA-F-]{16,}__(.+)$/.exec(name);
15187
+ const base = mcp ? `mcp__${mcp[1]}` : name;
15188
+ return base.length > MAX_TOOL_NAME ? base.slice(0, MAX_TOOL_NAME) : base;
15189
+ }
15190
+ function boundTools(tools) {
15191
+ const merged = {};
15192
+ for (const [name, count] of Object.entries(tools)) {
15193
+ const key = normalizeToolName(name);
15194
+ merged[key] = (merged[key] ?? 0) + count;
15195
+ }
15196
+ const entries = Object.entries(merged).sort((a, b) => b[1] - a[1]);
15197
+ if (entries.length <= MAX_REPORTED_TOOLS) return Object.fromEntries(entries);
15198
+ const out = Object.fromEntries(entries.slice(0, MAX_REPORTED_TOOLS));
15199
+ const tail = entries.slice(MAX_REPORTED_TOOLS);
15200
+ out.__other__ = tail.reduce((sum, [, count]) => sum + count, 0);
15201
+ out.__other_tools__ = tail.length;
15202
+ return out;
15203
+ }
15204
+ function toSummary(state) {
15205
+ const { firstTs, lastTs } = state;
15206
+ return {
15207
+ schema_version: SESSION_SUMMARY_SCHEMA_VERSION,
15208
+ source: "orgx_session_summary",
15209
+ session_id: state.session_id,
15210
+ source_client: state.source_client,
15211
+ repo: state.repo,
15212
+ cwd: state.cwd,
15213
+ day: firstTs === null ? "unknown" : new Date(firstTs).toISOString().slice(0, 10),
15214
+ started_at: firstTs === null ? null : new Date(firstTs).toISOString(),
15215
+ ended_at: lastTs === null ? null : new Date(lastTs).toISOString(),
15216
+ duration_min: firstTs !== null && lastTs !== null ? Math.round((lastTs - firstTs) / 6e4) : 0,
15217
+ events: state.events,
15218
+ prompts: state.prompts,
15219
+ tool_calls: state.tool_calls,
15220
+ tools: boundTools(state.tools),
15221
+ edits: state.edits,
15222
+ bash: state.bash
15223
+ };
15224
+ }
15225
+ function percentile(sorted, p) {
15226
+ if (sorted.length === 0) return 0;
15227
+ return sorted[Math.min(sorted.length - 1, Math.floor(sorted.length * p))] ?? 0;
15228
+ }
15229
+ async function distillSpool(spoolPath) {
15230
+ const sessions = /* @__PURE__ */ new Map();
15231
+ let lines = 0;
15232
+ let badLines = 0;
15233
+ const reader = createInterface({
15234
+ input: createReadStream(spoolPath),
15235
+ crlfDelay: Infinity
15236
+ });
15237
+ for await (const line of reader) {
15238
+ lines += 1;
15239
+ if (!line.trim()) continue;
15240
+ let record;
15241
+ try {
15242
+ record = JSON.parse(line);
15243
+ } catch {
15244
+ badLines += 1;
15245
+ continue;
15246
+ }
15247
+ const sessionId = typeof record.session_id === "string" ? record.session_id : "unknown";
15248
+ const cwd = typeof record.cwd === "string" ? record.cwd : null;
15249
+ const timestamp = typeof record.timestamp === "string" ? Date.parse(record.timestamp) : NaN;
15250
+ const ts = Number.isFinite(timestamp) ? timestamp : null;
15251
+ let state = sessions.get(sessionId);
15252
+ if (!state) {
15253
+ state = {
15254
+ session_id: sessionId,
15255
+ source_client: typeof record.source_client === "string" ? record.source_client : "unknown",
15256
+ repo: repoOf(cwd),
15257
+ cwd,
15258
+ firstTs: ts,
15259
+ lastTs: ts,
15260
+ events: 0,
15261
+ prompts: 0,
15262
+ tool_calls: 0,
15263
+ tools: {},
15264
+ edits: 0,
15265
+ bash: 0
15266
+ };
15267
+ sessions.set(sessionId, state);
15268
+ }
15269
+ state.events += 1;
15270
+ if (ts !== null) {
15271
+ if (state.firstTs === null || ts < state.firstTs) state.firstTs = ts;
15272
+ if (state.lastTs === null || ts > state.lastTs) state.lastTs = ts;
15273
+ }
15274
+ if (cwd && state.cwd === null) {
15275
+ state.cwd = cwd;
15276
+ state.repo = repoOf(cwd);
15277
+ }
15278
+ if (typeof record.source_client === "string" && state.source_client === "unknown") {
15279
+ state.source_client = record.source_client;
15280
+ }
15281
+ const event = typeof record.event === "string" ? record.event : "unknown";
15282
+ if (event === "UserPromptSubmit" || event.includes("user_prompt")) state.prompts += 1;
15283
+ const summary = record.summary;
15284
+ const tool = summary && typeof summary.tool_name === "string" ? summary.tool_name : null;
15285
+ if (tool && (event === "PostToolUse" || event.includes("post_tool_use"))) {
15286
+ state.tool_calls += 1;
15287
+ state.tools[tool] = (state.tools[tool] ?? 0) + 1;
15288
+ if (tool === "Write" || tool === "Edit" || tool === "NotebookEdit") state.edits += 1;
15289
+ if (tool === "Bash") state.bash += 1;
15290
+ }
15291
+ }
15292
+ const summaries = [...sessions.values()].map(toSummary).sort((a, b) => (a.started_at ?? "").localeCompare(b.started_at ?? ""));
15293
+ const sizes = summaries.map((summary) => Buffer.byteLength(JSON.stringify(summary), "utf8")).sort((a, b) => a - b);
15294
+ const total = sizes.reduce((sum, value) => sum + value, 0);
15295
+ let spoolBytes = 0;
15296
+ try {
15297
+ spoolBytes = statSync5(spoolPath).size;
15298
+ } catch {
15299
+ spoolBytes = 0;
15300
+ }
15301
+ return {
15302
+ spoolPath,
15303
+ spoolBytes,
15304
+ lines,
15305
+ badLines,
15306
+ summaries,
15307
+ bytes: {
15308
+ total,
15309
+ max: sizes.at(-1) ?? 0,
15310
+ p50: percentile(sizes, 0.5),
15311
+ p90: percentile(sizes, 0.9),
15312
+ mean: sizes.length > 0 ? Math.round(total / sizes.length) : 0
15313
+ }
15314
+ };
15315
+ }
15316
+ async function postSummaries(summaries, post, options = {}) {
15317
+ const maxConsecutiveFailures = options.maxConsecutiveFailures ?? 5;
15318
+ const result = { attempted: 0, posted: 0, failed: 0 };
15319
+ let consecutiveFailures = 0;
15320
+ for (const summary of summaries) {
15321
+ result.attempted += 1;
15322
+ const response = await post(summary);
15323
+ if (response.ok) {
15324
+ result.posted += 1;
15325
+ consecutiveFailures = 0;
15326
+ continue;
15327
+ }
15328
+ result.failed += 1;
15329
+ consecutiveFailures += 1;
15330
+ result.firstError ??= `HTTP ${response.status} for session ${summary.session_id}`;
15331
+ if (consecutiveFailures >= maxConsecutiveFailures) break;
15332
+ }
15333
+ return result;
15334
+ }
15335
+ function truncateSpool(spoolPath, post, options = {}) {
15336
+ if (!options.force && (post.failed > 0 || post.attempted === 0)) {
15337
+ return {
15338
+ truncated: false,
15339
+ reason: post.attempted === 0 ? "nothing was posted, so there is nothing safe to truncate" : `${post.failed} session(s) failed to post`,
15340
+ bytesReleased: 0
15341
+ };
15342
+ }
15343
+ let bytesReleased = 0;
15344
+ try {
15345
+ bytesReleased = statSync5(spoolPath).size;
15346
+ } catch {
15347
+ return { truncated: false, reason: "spool not found", bytesReleased: 0 };
15348
+ }
15349
+ const stamp = (options.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
15350
+ const archivePath = `${spoolPath}.backfilled-${stamp}`;
15351
+ renameSync2(spoolPath, archivePath);
15352
+ writeFileSync4(spoolPath, "", { encoding: "utf8", mode: 384 });
15353
+ return { truncated: true, archivePath, bytesReleased };
15354
+ }
15355
+
15356
+ // src/lib/session-summary-queue.ts
15357
+ var SESSION_SUMMARY_CAPTURE_VERSION = "orgx-session-summary-capture/v1";
15358
+ var SESSION_SUMMARY_CAPTURE_FILE_SUFFIX = ".capture.json";
15359
+ var SESSION_SUMMARY_CAPTURE_MAX_BYTES = 16 * 1024;
15360
+ function sessionSummaryCaptureFiles(queueDir) {
15361
+ if (!existsSync10(queueDir)) return [];
15362
+ try {
15363
+ return readdirSync7(queueDir).filter((name) => name.endsWith(SESSION_SUMMARY_CAPTURE_FILE_SUFFIX)).sort().map((name) => join9(queueDir, name));
15364
+ } catch {
15365
+ return [];
15366
+ }
15367
+ }
15368
+ function isRecord4(value) {
15369
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
15370
+ }
15371
+ function isSessionSummary(value) {
15372
+ if (!isRecord4(value)) return false;
15373
+ return typeof value.schema_version === "string" && typeof value.session_id === "string" && typeof value.source_client === "string";
15374
+ }
15375
+ function workGraphFingerprint(sessionId) {
15376
+ let left = 2166136261;
15377
+ let right = 16777619;
15378
+ for (let index = 0; index < sessionId.length; index += 1) {
15379
+ left = Math.imul(left ^ sessionId.charCodeAt(index), 16777619) >>> 0;
15380
+ right = Math.imul(
15381
+ right ^ sessionId.charCodeAt(sessionId.length - 1 - index),
15382
+ 2246822507
15383
+ ) >>> 0;
15384
+ }
15385
+ const seed = `${left.toString(16).padStart(8, "0")}${right.toString(16).padStart(8, "0")}`;
15386
+ return `wgf_${(seed + seed).slice(0, 24)}`;
15387
+ }
15388
+ function parseSessionSummaryCapture(raw) {
15389
+ if (Buffer.byteLength(raw, "utf8") > SESSION_SUMMARY_CAPTURE_MAX_BYTES) return null;
15390
+ try {
15391
+ const value = JSON.parse(raw);
15392
+ if (!isRecord4(value) || value.schema_version !== SESSION_SUMMARY_CAPTURE_VERSION) return null;
15393
+ if (typeof value.capture_id !== "string" || typeof value.queued_at !== "string" || !isSessionSummary(value.session)) {
15394
+ return null;
15395
+ }
15396
+ if (value.initiative_id !== void 0 && typeof value.initiative_id !== "string") {
15397
+ return null;
15398
+ }
15399
+ return {
15400
+ schema_version: SESSION_SUMMARY_CAPTURE_VERSION,
15401
+ capture_id: value.capture_id,
15402
+ // Captures created by wizard 0.1.56 before this field existed must remain
15403
+ // deliverable. Preserve the uncertainty instead of guessing that an old
15404
+ // snapshot represented a real SessionEnd.
15405
+ capture_kind: value.capture_kind === "turn_boundary" || value.capture_kind === "session_end" ? value.capture_kind : "legacy_unspecified",
15406
+ queued_at: value.queued_at,
15407
+ session: value.session,
15408
+ ...typeof value.initiative_id === "string" ? { initiative_id: value.initiative_id } : {}
15409
+ };
15410
+ } catch {
15411
+ return null;
15412
+ }
15413
+ }
15414
+ function readSessionSummaryCapture(path) {
15415
+ try {
15416
+ return parseSessionSummaryCapture(readFileSync8(path, "utf8"));
15417
+ } catch {
15418
+ return null;
15419
+ }
15420
+ }
15421
+ function buildCaptureEndpoint(path, baseUrl) {
15422
+ return buildOrgxApiUrl(path.replace(/^\/api/, ""), baseUrl);
15423
+ }
15424
+ function buildWorkGraphFallback(capture) {
15425
+ const summary = buildMinimalCloudSummary(capture.session, {
15426
+ captureId: capture.capture_id,
15427
+ captureKind: capture.capture_kind,
15428
+ queuedAt: capture.queued_at
15429
+ });
15430
+ return {
15431
+ report: {
15432
+ schema_version: "2.0.0",
15433
+ work_graph_fingerprint: workGraphFingerprint(summary.session_id),
15434
+ session_id: summary.session_id,
15435
+ investigation: { schema_version: "2.0.0" },
15436
+ raw_transcripts_sent: false,
15437
+ events: [
15438
+ {
15439
+ source_client: summary.source_client,
15440
+ session_id: summary.session_id,
15441
+ occurred_at: summary.ended_at,
15442
+ metadata: summary
15443
+ }
15444
+ ]
15445
+ }
15446
+ };
15447
+ }
15448
+ function deliveryBody(capture) {
15449
+ return {
15450
+ session: buildMinimalCloudSummary(capture.session, {
15451
+ captureId: capture.capture_id,
15452
+ captureKind: capture.capture_kind,
15453
+ queuedAt: capture.queued_at
15454
+ }),
15455
+ ...capture.initiative_id ? { initiative_id: capture.initiative_id } : {}
15456
+ };
15457
+ }
15458
+ async function flushSessionSummaryCaptures(options) {
15459
+ const files = sessionSummaryCaptureFiles(options.queueDir);
15460
+ const limit = Math.max(1, Math.min(options.limit ?? 100, 1e3));
15461
+ const selected = files.slice(0, limit);
15462
+ const headers = {
15463
+ Authorization: `Bearer ${options.auth.apiKey}`,
15464
+ "Content-Type": "application/json"
15465
+ };
15466
+ const primaryUrl = buildCaptureEndpoint(
15467
+ SESSION_SUMMARY_ENDPOINT_PATH,
15468
+ options.auth.baseUrl
15469
+ );
15470
+ const fallbackUrl = buildCaptureEndpoint(
15471
+ SESSION_SUMMARY_FALLBACK_ENDPOINT_PATH,
15472
+ options.auth.baseUrl
15473
+ );
15474
+ const result = {
15475
+ found: files.length,
15476
+ attempted: 0,
15477
+ acknowledged: 0,
15478
+ retained: 0,
15479
+ malformed: 0,
15480
+ fallbackAcknowledged: 0
15481
+ };
15482
+ for (const path of selected) {
15483
+ const capture = readSessionSummaryCapture(path);
15484
+ if (!capture) {
15485
+ result.malformed += 1;
15486
+ result.retained += 1;
15487
+ result.firstError ??= `Malformed capture retained: ${path}`;
15488
+ continue;
15489
+ }
15490
+ result.attempted += 1;
15491
+ let response;
15492
+ let usedFallback = false;
15493
+ try {
15494
+ response = await options.send(primaryUrl, deliveryBody(capture), headers);
15495
+ if (!response.ok && (response.status === 404 || response.status === 405)) {
15496
+ response = await options.send(fallbackUrl, buildWorkGraphFallback(capture), headers);
15497
+ usedFallback = true;
15498
+ }
15499
+ } catch {
15500
+ response = { ok: false, status: 0 };
15501
+ }
15502
+ if (!response.ok) {
15503
+ result.retained += 1;
15504
+ result.firstError ??= `HTTP ${response.status} for ${capture.capture_id}`;
15505
+ if (options.stopOnFailure) break;
15506
+ continue;
15507
+ }
15508
+ try {
15509
+ unlinkSync2(path);
15510
+ result.acknowledged += 1;
15511
+ if (usedFallback) result.fallbackAcknowledged += 1;
15512
+ } catch {
15513
+ result.retained += 1;
15514
+ result.firstError ??= `Acknowledged capture could not be removed: ${path}`;
15515
+ }
15516
+ }
15517
+ return result;
15518
+ }
15519
+
15520
+ // src/lib/session-summary-flush-lease.ts
15521
+ import { randomUUID as randomUUID3 } from "crypto";
15522
+ import {
15523
+ closeSync as closeSync2,
15524
+ fsyncSync,
15525
+ mkdirSync as mkdirSync5,
15526
+ openSync as openSync2,
15527
+ readFileSync as readFileSync9,
15528
+ renameSync as renameSync3,
15529
+ statSync as statSync6,
15530
+ unlinkSync as unlinkSync3,
15531
+ writeSync
15532
+ } from "fs";
15533
+ import { join as join10 } from "path";
15534
+ var SESSION_SUMMARY_FLUSH_LOCK_NAME = ".flush.lock";
15535
+ var SESSION_SUMMARY_FLUSH_MAX_AGE_MS = 2 * 60 * 60 * 1e3;
15536
+ var MALFORMED_LOCK_GRACE_MS = 5 * 60 * 1e3;
15537
+ function errorCode(error) {
15538
+ return error && typeof error === "object" && "code" in error ? String(error.code) : null;
15539
+ }
15540
+ function readLock(path) {
15541
+ try {
15542
+ const value = JSON.parse(readFileSync9(path, "utf8"));
15543
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
15544
+ const record = value;
15545
+ return typeof record.token === "string" && typeof record.pid === "number" && Number.isInteger(record.pid) && typeof record.claimed_at === "string" ? {
15546
+ token: record.token,
15547
+ pid: record.pid,
15548
+ claimed_at: record.claimed_at
15549
+ } : null;
15550
+ } catch {
15551
+ return null;
15552
+ }
15553
+ }
15554
+ function processIsAlive(pid) {
15555
+ if (!Number.isInteger(pid) || pid <= 0) return false;
15556
+ try {
15557
+ process.kill(pid, 0);
15558
+ return true;
15559
+ } catch (error) {
15560
+ return errorCode(error) === "EPERM";
15561
+ }
15562
+ }
15563
+ function lockAgeMs(path, now) {
15564
+ try {
15565
+ return Math.max(0, now.getTime() - statSync6(path).mtimeMs);
15566
+ } catch {
15567
+ return null;
15568
+ }
15569
+ }
15570
+ function shouldReclaimLock(path, now, isProcessAlive) {
15571
+ const age = lockAgeMs(path, now);
15572
+ if (age === null) return true;
15573
+ if (age >= SESSION_SUMMARY_FLUSH_MAX_AGE_MS) return true;
15574
+ const record = readLock(path);
15575
+ if (!record) return age >= MALFORMED_LOCK_GRACE_MS;
15576
+ return !isProcessAlive(record.pid);
15577
+ }
15578
+ function writeLock(path, record) {
15579
+ let descriptor = null;
15580
+ try {
15581
+ descriptor = openSync2(path, "wx", 384);
15582
+ writeSync(descriptor, JSON.stringify(record), void 0, "utf8");
15583
+ fsyncSync(descriptor);
15584
+ return "acquired";
15585
+ } catch (error) {
15586
+ return errorCode(error) === "EEXIST" ? "exists" : "unavailable";
15587
+ } finally {
15588
+ if (descriptor !== null) closeSync2(descriptor);
15589
+ }
15590
+ }
15591
+ function releaseOwnedLock(path, token) {
15592
+ try {
15593
+ if (readLock(path)?.token === token) unlinkSync3(path);
15594
+ } catch {
15595
+ }
15596
+ }
15597
+ function claimSessionSummaryFlushLease(queueDir, options = {}) {
15598
+ const now = options.now ?? /* @__PURE__ */ new Date();
15599
+ const pid = options.pid ?? process.pid;
15600
+ const token = options.token ?? randomUUID3();
15601
+ const isProcessAlive = options.isProcessAlive ?? processIsAlive;
15602
+ const path = join10(queueDir, SESSION_SUMMARY_FLUSH_LOCK_NAME);
15603
+ const record = { token, pid, claimed_at: now.toISOString() };
15604
+ try {
15605
+ mkdirSync5(queueDir, { recursive: true, mode: 448 });
15606
+ } catch {
15607
+ return { acquired: false, reason: "unavailable" };
15608
+ }
15609
+ const firstAttempt = writeLock(path, record);
15610
+ if (firstAttempt === "acquired") {
15611
+ return {
15612
+ acquired: true,
15613
+ path,
15614
+ release: () => releaseOwnedLock(path, token)
15615
+ };
15616
+ }
15617
+ if (firstAttempt === "unavailable") {
15618
+ return { acquired: false, reason: "unavailable" };
15619
+ }
15620
+ if (!shouldReclaimLock(path, now, isProcessAlive)) {
15621
+ return { acquired: false, reason: "busy" };
15622
+ }
15623
+ const stalePath = `${path}.stale-${token}`;
15624
+ try {
15625
+ renameSync3(path, stalePath);
15626
+ unlinkSync3(stalePath);
15627
+ } catch {
15628
+ return { acquired: false, reason: "busy" };
15629
+ }
15630
+ const retry = writeLock(path, record);
15631
+ if (retry !== "acquired") {
15632
+ return {
15633
+ acquired: false,
15634
+ reason: retry === "exists" ? "busy" : "unavailable"
15635
+ };
15636
+ }
15637
+ return {
15638
+ acquired: true,
15639
+ path,
15640
+ release: () => releaseOwnedLock(path, token)
15641
+ };
15642
+ }
15643
+
14088
15644
  // src/spinner.ts
14089
15645
  import ora from "ora";
14090
15646
  import pc2 from "picocolors";
@@ -14100,40 +15656,40 @@ function createOrgxSpinner(text2) {
14100
15656
  }
14101
15657
 
14102
15658
  // src/lib/workload-diagnosis.ts
14103
- import { closeSync as closeSync2, openSync as openSync2, readSync as readSync2 } from "fs";
14104
- import { resolve as resolve2 } from "path";
15659
+ import { closeSync as closeSync3, openSync as openSync3, readSync as readSync2 } from "fs";
15660
+ import { resolve as resolve3 } from "path";
14105
15661
 
14106
15662
  // src/lib/workload-diagnosis-schema.ts
14107
- import { z } from "zod";
15663
+ import { z as z2 } from "zod";
14108
15664
  var WORKLOAD_DIAGNOSIS_SCHEMA_VERSION = "workload-diagnosis/0.1";
14109
15665
  var SECRET_PATTERN = /(?:\b(?:api[_-]?key|authorization|cookie|access[_-]?token|refresh[_-]?token|client[_-]?secret|password|secret|session|token)\s*[:=]\s*\S+|\bbearer\s+[a-z0-9._~+/=-]{8,}|\b(?:oxk_[a-z0-9_-]{8,}|sk-(?:live|test|proj)?[_-]?[a-z0-9_-]{8,}|[sr]k_(?:live|test)_[a-z0-9_-]{8,}|gh[pousr]_[a-z0-9]{8,}|github_pat_[a-z0-9_]{8,}|glpat-[a-z0-9_-]{16,}|xox[baprs]-[a-z0-9-]{8,}|npm_[a-z0-9]{8,}|sntrys_[a-z0-9_-]{8,}|whsec_[a-z0-9_-]{8,}|SG\.[a-z0-9_-]{16,}\.[a-z0-9_-]{16,}|pypi-[a-z0-9_-]{24,}|dop_v1_[a-f0-9]{16,}|hf_[a-z0-9]{20,}|A(?:KI|SI)A[A-Z0-9]{16}|AIza[a-z0-9_-]{20,})\b|\beyJ[a-z0-9_-]{8,}\.eyJ[a-z0-9_-]{8,}\.[a-z0-9_-]{8,}|[a-z][a-z0-9+.-]*:\/\/[^\s/@:]+:[^\s/@]+@|-----BEGIN [A-Z ]*PRIVATE KEY-----)/i;
14110
15666
  var BASIC_AUTH_SECRET_PATTERN = /\bbasic\s+(?:[a-z0-9+/]{4})*(?:[a-z0-9+/]{4}|[a-z0-9+/]{2}==|[a-z0-9+/]{3}=)(?=$|[^a-z0-9+/=])/i;
14111
15667
  function containsCredentialPattern(value) {
14112
15668
  return SECRET_PATTERN.test(value) || BASIC_AUTH_SECRET_PATTERN.test(value);
14113
15669
  }
14114
- var SafeSummarySchema = z.string().trim().min(1).max(500).refine(
15670
+ var SafeSummarySchema = z2.string().trim().min(1).max(500).refine(
14115
15671
  (value) => !/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/.test(value),
14116
15672
  { message: "Control characters are not allowed" }
14117
15673
  ).refine((value) => !containsCredentialPattern(value), {
14118
15674
  message: "Do not include credentials, tokens, passwords, or private keys"
14119
15675
  });
14120
- var SafeResponseText = (max) => z.string().min(1).max(max).refine((value) => !/[\u0000-\u001f\u007f-\u009f]/.test(value), {
15676
+ var SafeResponseText = (max) => z2.string().min(1).max(max).refine((value) => !/[\u0000-\u001f\u007f-\u009f]/.test(value), {
14121
15677
  message: "Control characters are not allowed"
14122
15678
  });
14123
- var WorkloadTimeHorizonSchema = z.enum([
15679
+ var WorkloadTimeHorizonSchema = z2.enum([
14124
15680
  "single_turn",
14125
15681
  "single_session",
14126
15682
  "multi_day",
14127
15683
  "recurring",
14128
15684
  "continuous"
14129
15685
  ]);
14130
- var WorkloadCoordinationSchema = z.enum([
15686
+ var WorkloadCoordinationSchema = z2.enum([
14131
15687
  "none",
14132
15688
  "handoff",
14133
15689
  "parallel",
14134
15690
  "hierarchical"
14135
15691
  ]);
14136
- var WorkloadSystemCategorySchema = z.enum([
15692
+ var WorkloadSystemCategorySchema = z2.enum([
14137
15693
  "code_repository",
14138
15694
  "issue_tracker",
14139
15695
  "document_store",
@@ -14147,20 +15703,20 @@ var WorkloadSystemCategorySchema = z.enum([
14147
15703
  "identity",
14148
15704
  "other"
14149
15705
  ]);
14150
- var WorkloadAccessModeSchema = z.enum(["read", "write", "admin"]);
14151
- var WorkloadSideEffectSchema = z.enum([
15706
+ var WorkloadAccessModeSchema = z2.enum(["read", "write", "admin"]);
15707
+ var WorkloadSideEffectSchema = z2.enum([
14152
15708
  "none",
14153
15709
  "reversible",
14154
15710
  "external",
14155
15711
  "irreversible"
14156
15712
  ]);
14157
- var WorkloadDataClassSchema = z.enum([
15713
+ var WorkloadDataClassSchema = z2.enum([
14158
15714
  "public",
14159
15715
  "internal",
14160
15716
  "confidential",
14161
15717
  "regulated"
14162
15718
  ]);
14163
- var WorkloadActionSchema = z.enum([
15719
+ var WorkloadActionSchema = z2.enum([
14164
15720
  "research",
14165
15721
  "draft",
14166
15722
  "read_internal_data",
@@ -14173,28 +15729,28 @@ var WorkloadActionSchema = z.enum([
14173
15729
  "change_permissions",
14174
15730
  "delete_data"
14175
15731
  ]);
14176
- var WorkloadApprovalPolicySchema = z.enum([
15732
+ var WorkloadApprovalPolicySchema = z2.enum([
14177
15733
  "not_applicable",
14178
15734
  "per_action",
14179
15735
  "sensitive_actions",
14180
15736
  "exceptions_only",
14181
15737
  "undefined"
14182
15738
  ]);
14183
- var WorkloadBudgetControlSchema = z.enum([
15739
+ var WorkloadBudgetControlSchema = z2.enum([
14184
15740
  "not_applicable",
14185
15741
  "fixed_limit",
14186
15742
  "dynamic_limit",
14187
15743
  "unbounded",
14188
15744
  "undefined"
14189
15745
  ]);
14190
- var SystemAccessSchema = z.object({
15746
+ var SystemAccessSchema = z2.object({
14191
15747
  category: WorkloadSystemCategorySchema,
14192
15748
  access: WorkloadAccessModeSchema,
14193
15749
  side_effect: WorkloadSideEffectSchema,
14194
15750
  data_class: WorkloadDataClassSchema
14195
15751
  }).strict();
14196
- var AuthoritySchema = z.object({
14197
- actions: z.array(WorkloadActionSchema).max(11).superRefine((actions, context) => {
15752
+ var AuthoritySchema = z2.object({
15753
+ actions: z2.array(WorkloadActionSchema).max(11).superRefine((actions, context) => {
14198
15754
  if (new Set(actions).size !== actions.length) {
14199
15755
  context.addIssue({
14200
15756
  code: "custom",
@@ -14231,21 +15787,21 @@ var AuthoritySchema = z.object({
14231
15787
  });
14232
15788
  }
14233
15789
  });
14234
- var AccountabilitySchema = z.object({
14235
- evidence: z.enum(["none", "activity_log", "artifact", "verified_outcome"]),
14236
- acceptance: z.enum(["none", "agent", "human", "downstream_system"]),
14237
- consequence: z.enum(["low", "moderate", "high", "regulated"]),
14238
- retention: z.enum(["none", "short_term", "long_term", "regulated"])
15790
+ var AccountabilitySchema = z2.object({
15791
+ evidence: z2.enum(["none", "activity_log", "artifact", "verified_outcome"]),
15792
+ acceptance: z2.enum(["none", "agent", "human", "downstream_system"]),
15793
+ consequence: z2.enum(["low", "moderate", "high", "regulated"]),
15794
+ retention: z2.enum(["none", "short_term", "long_term", "regulated"])
14239
15795
  }).strict();
14240
- var WorkloadDiagnosisRequestSchema = z.object({
14241
- schema_version: z.literal(WORKLOAD_DIAGNOSIS_SCHEMA_VERSION),
14242
- workload: z.object({
15796
+ var WorkloadDiagnosisRequestSchema = z2.object({
15797
+ schema_version: z2.literal(WORKLOAD_DIAGNOSIS_SCHEMA_VERSION),
15798
+ workload: z2.object({
14243
15799
  name: SafeSummarySchema.max(120),
14244
15800
  outcome: SafeSummarySchema,
14245
15801
  time_horizon: WorkloadTimeHorizonSchema,
14246
- agent_count: z.number().int().min(1).max(64),
15802
+ agent_count: z2.number().int().min(1).max(64),
14247
15803
  coordination: WorkloadCoordinationSchema,
14248
- systems: z.array(SystemAccessSchema).max(12).superRefine((systems, context) => {
15804
+ systems: z2.array(SystemAccessSchema).max(12).superRefine((systems, context) => {
14249
15805
  const seen = /* @__PURE__ */ new Set();
14250
15806
  systems.forEach((system, index) => {
14251
15807
  if (seen.has(system.category)) {
@@ -14325,28 +15881,28 @@ var WorkloadDiagnosisRequestSchema = z.object({
14325
15881
  }
14326
15882
  });
14327
15883
  });
14328
- var BoundaryNameSchema = z.enum([
15884
+ var BoundaryNameSchema = z2.enum([
14329
15885
  "time",
14330
15886
  "agents",
14331
15887
  "systems",
14332
15888
  "authority",
14333
15889
  "accountability"
14334
15890
  ]);
14335
- var BoundaryFindingSchema = z.object({
14336
- state: z.enum(["absent", "present", "critical"]),
14337
- score: z.number().int().min(0).max(2),
15891
+ var BoundaryFindingSchema = z2.object({
15892
+ state: z2.enum(["absent", "present", "critical"]),
15893
+ score: z2.number().int().min(0).max(2),
14338
15894
  reason: SafeResponseText(300)
14339
15895
  }).strict();
14340
- var ProposedResourceSchema = z.object({
15896
+ var ProposedResourceSchema = z2.object({
14341
15897
  resource: SafeResponseText(80),
14342
15898
  requested_access: WorkloadAccessModeSchema,
14343
- scope_intents: z.array(SafeResponseText(80)).min(1).max(12),
15899
+ scope_intents: z2.array(SafeResponseText(80)).min(1).max(12),
14344
15900
  purpose: SafeResponseText(300),
14345
- credential_input_required: z.literal(false)
15901
+ credential_input_required: z2.literal(false)
14346
15902
  }).strict();
14347
- var HumanApprovalSchema = z.object({
14348
- id: z.string().regex(/^[a-z0-9_-]{1,80}$/),
14349
- owner_role: z.enum([
15903
+ var HumanApprovalSchema = z2.object({
15904
+ id: z2.string().regex(/^[a-z0-9_-]{1,80}$/),
15905
+ owner_role: z2.enum([
14350
15906
  "workload_owner",
14351
15907
  "system_owner",
14352
15908
  "code_owner",
@@ -14355,63 +15911,63 @@ var HumanApprovalSchema = z.object({
14355
15911
  "identity_admin",
14356
15912
  "data_owner"
14357
15913
  ]),
14358
- timing: z.enum([
15914
+ timing: z2.enum([
14359
15915
  "before_installation",
14360
15916
  "before_first_use",
14361
15917
  "per_action",
14362
15918
  "when_threshold_exceeded"
14363
15919
  ]),
14364
15920
  decision: SafeResponseText(300),
14365
- scope: z.array(SafeResponseText(100)).min(1).max(16)
15921
+ scope: z2.array(SafeResponseText(100)).min(1).max(16)
14366
15922
  }).strict();
14367
- var WorkloadDiagnosisResponseSchema = z.object({
14368
- schema_version: z.literal(WORKLOAD_DIAGNOSIS_SCHEMA_VERSION),
14369
- diagnosis_id: z.string().regex(/^wdg_[a-f0-9]{24}$/),
14370
- recommendation: z.object({
14371
- verdict: z.enum(["needed", "conditional", "not_needed"]),
14372
- mode: z.enum(["none", "receipt_only", "governed_workspace"]),
15923
+ var WorkloadDiagnosisResponseSchema = z2.object({
15924
+ schema_version: z2.literal(WORKLOAD_DIAGNOSIS_SCHEMA_VERSION),
15925
+ diagnosis_id: z2.string().regex(/^wdg_[a-f0-9]{24}$/),
15926
+ recommendation: z2.object({
15927
+ verdict: z2.enum(["needed", "conditional", "not_needed"]),
15928
+ mode: z2.enum(["none", "receipt_only", "governed_workspace"]),
14373
15929
  summary: SafeResponseText(400),
14374
- rationale: z.array(SafeResponseText(300)).max(5),
14375
- active_boundary_count: z.number().int().min(0).max(5)
15930
+ rationale: z2.array(SafeResponseText(300)).max(5),
15931
+ active_boundary_count: z2.number().int().min(0).max(5)
14376
15932
  }).strict(),
14377
- boundaries: z.object({
15933
+ boundaries: z2.object({
14378
15934
  time: BoundaryFindingSchema,
14379
15935
  agents: BoundaryFindingSchema,
14380
15936
  systems: BoundaryFindingSchema,
14381
15937
  authority: BoundaryFindingSchema,
14382
15938
  accountability: BoundaryFindingSchema
14383
15939
  }).strict(),
14384
- missing_capabilities: z.array(
14385
- z.object({
14386
- id: z.string().regex(/^[a-z0-9_]{1,60}$/),
15940
+ missing_capabilities: z2.array(
15941
+ z2.object({
15942
+ id: z2.string().regex(/^[a-z0-9_]{1,60}$/),
14387
15943
  boundary: BoundaryNameSchema,
14388
15944
  reason: SafeResponseText(300)
14389
15945
  }).strict()
14390
15946
  ).max(15),
14391
- proposed_resources: z.array(ProposedResourceSchema).max(13),
14392
- what_remains_local: z.array(
14393
- z.object({
15947
+ proposed_resources: z2.array(ProposedResourceSchema).max(13),
15948
+ what_remains_local: z2.array(
15949
+ z2.object({
14394
15950
  item: SafeResponseText(100),
14395
15951
  reason: SafeResponseText(300)
14396
15952
  }).strict()
14397
15953
  ).min(1).max(6),
14398
- risks: z.array(
14399
- z.object({
14400
- id: z.string().regex(/^[a-z0-9_]{1,60}$/),
14401
- severity: z.enum(["low", "medium", "high", "critical"]),
15954
+ risks: z2.array(
15955
+ z2.object({
15956
+ id: z2.string().regex(/^[a-z0-9_]{1,60}$/),
15957
+ severity: z2.enum(["low", "medium", "high", "critical"]),
14402
15958
  boundary: BoundaryNameSchema,
14403
15959
  description: SafeResponseText(300),
14404
15960
  mitigation: SafeResponseText(300)
14405
15961
  }).strict()
14406
15962
  ).max(15),
14407
- capabilities_after_installation: z.array(SafeResponseText(300)).max(10),
14408
- human_approvals: z.array(HumanApprovalSchema).max(32),
14409
- approval_handoff: z.object({
14410
- kind: z.enum(["none", "browser_review"]),
14411
- url: z.string().url().nullable(),
14412
- mutates_state: z.literal(false),
14413
- carries_sensitive_data: z.boolean(),
14414
- approval_ids: z.array(z.string().regex(/^[a-z0-9_-]{1,80}$/)).max(32)
15963
+ capabilities_after_installation: z2.array(SafeResponseText(300)).max(10),
15964
+ human_approvals: z2.array(HumanApprovalSchema).max(32),
15965
+ approval_handoff: z2.object({
15966
+ kind: z2.enum(["none", "browser_review"]),
15967
+ url: z2.string().url().nullable(),
15968
+ mutates_state: z2.literal(false),
15969
+ carries_sensitive_data: z2.boolean(),
15970
+ approval_ids: z2.array(z2.string().regex(/^[a-z0-9_-]{1,80}$/)).max(32)
14415
15971
  }).strict()
14416
15972
  }).strict().superRefine((response, context) => {
14417
15973
  const activeCount = Object.values(response.boundaries).filter(
@@ -14454,7 +16010,7 @@ var MAX_HANDOFF_TOKEN_CHARACTERS = 7e3;
14454
16010
  var SENSITIVE_KEY2 = /(?:^|[_-])(?:authorization|cookie|credentials?|password|secrets?|session|tokens?|api[_-]?keys?|access[_-]?tokens?|refresh[_-]?tokens?|client[_-]?secrets?|private[_-]?keys?|database[_-]?(?:url|uri)|db[_-]?(?:url|uri))(?:$|[_-])/i;
14455
16011
  function readBoundedUtf8(source) {
14456
16012
  const shouldClose = source !== "-";
14457
- const fd = shouldClose ? openSync2(resolve2(source), "r") : 0;
16013
+ const fd = shouldClose ? openSync3(resolve3(source), "r") : 0;
14458
16014
  const buffer = Buffer.alloc(MAX_WORKLOAD_DIAGNOSIS_REQUEST_BYTES + 1);
14459
16015
  let offset = 0;
14460
16016
  try {
@@ -14470,7 +16026,7 @@ function readBoundedUtf8(source) {
14470
16026
  offset += bytesRead;
14471
16027
  }
14472
16028
  } finally {
14473
- if (shouldClose) closeSync2(fd);
16029
+ if (shouldClose) closeSync3(fd);
14474
16030
  }
14475
16031
  if (offset > MAX_WORKLOAD_DIAGNOSIS_REQUEST_BYTES) {
14476
16032
  throw new Error(
@@ -14825,8 +16381,171 @@ function printRuntimeHookInspection(report) {
14825
16381
  console.log(` ${report.codex.hooksEnabled ? ICON.ok : ICON.warn} ${pc3.bold("Codex flag ")} ${report.codex.hooksEnabled ? pc3.green("enabled") : pc3.yellow("not enabled")} ${pc3.dim(report.paths.codexConfigPath)}`);
14826
16382
  console.log(` ${report.codex.notifyPreserved ? ICON.ok : ICON.warn} ${pc3.bold("notify ")} ${report.codex.notifyPreserved ? pc3.green("preserved") : pc3.yellow("check config")} ${pc3.dim(report.codex.hasNotify ? "existing notify detected" : "no notify entry")}`);
14827
16383
  console.log(` ${report.installed.claudeCode ? ICON.ok : ICON.warn} ${pc3.bold("Claude Code ")} ${report.installed.claudeCode ? pc3.green("installed") : pc3.yellow("missing")} ${pc3.dim(report.paths.claudeSettingsPath)}`);
16384
+ console.log(` ${report.installed.summaryHookScript ? ICON.ok : ICON.warn} ${pc3.bold("summary hook")} ${report.installed.summaryHookScript ? pc3.green("installed") : pc3.yellow("missing")} ${pc3.dim(report.paths.summaryHookScriptPath)}`);
16385
+ console.log(` ${report.installed.automaticDelivery ? ICON.ok : ICON.warn} ${pc3.bold("auto delivery")} ${report.installed.automaticDelivery ? pc3.green("configured") : pc3.yellow("not configured")} ${pc3.dim("detached, ACK-gated worker")}`);
16386
+ console.log(` ${ICON.skip} ${pc3.bold("capture queue")} ${pc3.dim(`${report.sessionSummaryCaptures} capture${report.sessionSummaryCaptures === 1 ? "" : "s"} at ${report.paths.sessionSummaryQueueDir}`)}`);
14828
16387
  console.log(` ${ICON.skip} ${pc3.bold("outbox ")} ${pc3.dim(`${report.outboxEvents} event${report.outboxEvents === 1 ? "" : "s"} at ${report.paths.outboxPath}`)}`);
14829
16388
  }
16389
+ function printSessionSummaryFlush(result) {
16390
+ if (result.skipped) {
16391
+ console.log(` ${ICON.skip} ${pc3.bold("captures ")} ${pc3.dim(result.skipped.replaceAll("_", " "))}`);
16392
+ return;
16393
+ }
16394
+ const delivered = `${result.acknowledged}/${result.attempted} acknowledged`;
16395
+ console.log(` ${result.retained === 0 ? ICON.ok : ICON.warn} ${pc3.bold("captures ")} ${pc3.dim(`${delivered}, ${result.retained} retained, ${result.malformed} malformed`)}`);
16396
+ if (result.fallbackAcknowledged > 0) {
16397
+ console.log(` ${ICON.warn} ${pc3.bold("fallback ")} ${pc3.dim(`${result.fallbackAcknowledged} capture(s) delivered through the legacy Work Graph endpoint`)}`);
16398
+ }
16399
+ if (result.firstError) console.log(` ${ICON.warn} ${pc3.dim(result.firstError)}`);
16400
+ }
16401
+ async function runSessionSummaryFlushCommand(options) {
16402
+ const queueDir = resolve4(options.queue?.trim() || inspectRuntimeHooks().paths.sessionSummaryQueueDir);
16403
+ const found = sessionSummaryCaptureFiles(queueDir).length;
16404
+ let result;
16405
+ if (found === 0) {
16406
+ result = {
16407
+ found: 0,
16408
+ attempted: 0,
16409
+ acknowledged: 0,
16410
+ retained: 0,
16411
+ malformed: 0,
16412
+ fallbackAcknowledged: 0,
16413
+ skipped: "empty_queue"
16414
+ };
16415
+ } else {
16416
+ const lease = claimSessionSummaryFlushLease(queueDir);
16417
+ if (!lease.acquired) {
16418
+ if (lease.reason === "unavailable") {
16419
+ if (options.background) return;
16420
+ throw new Error(`Could not acquire the session-summary flush lease at ${queueDir}.`);
16421
+ }
16422
+ result = {
16423
+ found,
16424
+ attempted: 0,
16425
+ acknowledged: 0,
16426
+ retained: 0,
16427
+ malformed: 0,
16428
+ fallbackAcknowledged: 0,
16429
+ skipped: "already_running"
16430
+ };
16431
+ } else {
16432
+ try {
16433
+ const auth = await resolveOrgxAuth();
16434
+ if (!auth) {
16435
+ if (options.background) return;
16436
+ throw new Error("No OrgX credential found. Run `orgx-wizard login` first.");
16437
+ }
16438
+ result = await flushSessionSummaryCaptures({
16439
+ queueDir,
16440
+ auth,
16441
+ limit: parsePositiveInt(options.limit, 100),
16442
+ stopOnFailure: options.background === true,
16443
+ send: async (url, body, headers) => {
16444
+ try {
16445
+ const response = await fetchWithRetry(
16446
+ url,
16447
+ {
16448
+ method: "POST",
16449
+ headers,
16450
+ body: JSON.stringify(body)
16451
+ },
16452
+ options.background ? { timeoutMs: 8e3, retries: 0 } : { timeoutMs: 12e3, retries: 1 }
16453
+ );
16454
+ return { ok: response.ok, status: response.status };
16455
+ } catch {
16456
+ return { ok: false, status: 0 };
16457
+ }
16458
+ }
16459
+ });
16460
+ } finally {
16461
+ lease.release();
16462
+ }
16463
+ }
16464
+ }
16465
+ if (options.background) return;
16466
+ await safeTrackWizardTelemetry("hooks_session_summary_flush_ran", {
16467
+ command: "hooks flush",
16468
+ attempted: String(result.attempted),
16469
+ acknowledged: String(result.acknowledged),
16470
+ retained: String(result.retained)
16471
+ });
16472
+ if (result.retained > 0) process.exitCode = 1;
16473
+ if (options.json) {
16474
+ console.log(JSON.stringify({ ok: result.retained === 0, queueDir, ...result }, null, 2));
16475
+ return;
16476
+ }
16477
+ printSessionSummaryFlush(result);
16478
+ }
16479
+ async function runHookBackfillCommand(options) {
16480
+ const paths = inspectRuntimeHooks().paths;
16481
+ const spoolPath = resolve4(options.spool?.trim() || paths.outboxPath);
16482
+ if (!fileExists(spoolPath)) {
16483
+ const message = `No hook spool found at ${spoolPath}`;
16484
+ if (options.json) {
16485
+ console.log(JSON.stringify({ ok: true, distilled: 0, reason: "missing_spool", spoolPath }, null, 2));
16486
+ return;
16487
+ }
16488
+ console.log(` ${ICON.skip} ${pc3.dim(message)}`);
16489
+ return;
16490
+ }
16491
+ if ((options.post || options.truncate) && !options.yes) {
16492
+ throw new Error("Posting or truncating the hook spool requires --yes. Run without --post to preview.");
16493
+ }
16494
+ if (options.truncate && !options.post) {
16495
+ throw new Error("--truncate is only valid together with --post; nothing may be discarded unsent.");
16496
+ }
16497
+ const distilled = await distillSpool(spoolPath);
16498
+ const reduction = distilled.bytes.total > 0 ? Math.round(distilled.spoolBytes / distilled.bytes.total) : 0;
16499
+ let post = { attempted: 0, posted: 0, failed: 0 };
16500
+ let truncation = { truncated: false, bytesReleased: 0 };
16501
+ if (options.post) {
16502
+ const auth = await resolveOrgxAuth();
16503
+ if (!auth) {
16504
+ throw new Error("No OrgX credential found. Run `orgx-wizard login` first.");
16505
+ }
16506
+ const url = `${auth.baseUrl.replace(/\/+$/, "")}${SESSION_SUMMARY_ENDPOINT_PATH}`;
16507
+ post = await postSummaries(distilled.summaries, async (summary) => {
16508
+ const minimalSummary = buildMinimalCloudSummary(summary, {
16509
+ captureKind: "historical_backfill"
16510
+ });
16511
+ const response = await fetchWithRetry(url, {
16512
+ method: "POST",
16513
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${auth.apiKey}` },
16514
+ body: JSON.stringify({ session: minimalSummary, backfill: true })
16515
+ });
16516
+ return { ok: response.ok, status: response.status };
16517
+ });
16518
+ if (options.truncate) {
16519
+ truncation = truncateSpool(spoolPath, post);
16520
+ }
16521
+ }
16522
+ const result = {
16523
+ ok: true,
16524
+ spoolPath,
16525
+ spoolBytes: distilled.spoolBytes,
16526
+ lines: distilled.lines,
16527
+ badLines: distilled.badLines,
16528
+ sessions: distilled.summaries.length,
16529
+ payloadBytes: distilled.bytes,
16530
+ reductionFactor: reduction,
16531
+ posted: options.post ? post : null,
16532
+ truncated: options.truncate ? truncation : null
16533
+ };
16534
+ if (options.json) {
16535
+ console.log(JSON.stringify(result, null, 2));
16536
+ return;
16537
+ }
16538
+ console.log(` ${ICON.ok} ${pc3.bold("spool ")} ${pc3.dim(`${(distilled.spoolBytes / 1048576).toFixed(1)}MB, ${distilled.lines} lines, ${distilled.badLines} unparsable`)}`);
16539
+ console.log(` ${ICON.ok} ${pc3.bold("distilled ")} ${pc3.dim(`${distilled.summaries.length} sessions, ${(distilled.bytes.total / 1024).toFixed(0)}KB total (p50 ${distilled.bytes.p50}B, max ${distilled.bytes.max}B), ${reduction}x smaller`)}`);
16540
+ if (!options.post) {
16541
+ console.log(` ${ICON.skip} ${pc3.dim("preview only. Re-run with --post --yes to upload, and add --truncate to archive the spool.")}`);
16542
+ return;
16543
+ }
16544
+ console.log(` ${post.failed === 0 ? ICON.ok : ICON.warn} ${pc3.bold("posted ")} ${pc3.dim(`${post.posted}/${post.attempted} sessions${post.firstError ? ` (${post.firstError})` : ""}`)}`);
16545
+ if (options.truncate) {
16546
+ console.log(` ${truncation.truncated ? ICON.ok : ICON.warn} ${pc3.bold("truncated ")} ${pc3.dim(truncation.truncated ? `${(truncation.bytesReleased / 1048576).toFixed(1)}MB archived to ${truncation.archivePath}` : `skipped: ${truncation.reason}`)}`);
16547
+ }
16548
+ }
14830
16549
  function requireHookReplayApproval(options, interactive) {
14831
16550
  if (options.yes) return true;
14832
16551
  if (!interactive) {
@@ -14857,7 +16576,7 @@ async function runHookReplayCommand(options) {
14857
16576
  }
14858
16577
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
14859
16578
  const paths = inspectRuntimeHooks().paths;
14860
- const outboxPath = resolve3(options.outbox?.trim() || paths.outboxPath);
16579
+ const outboxPath = resolve4(options.outbox?.trim() || paths.outboxPath);
14861
16580
  const readResult = readRuntimeHookOutbox(outboxPath, parsePositiveInt(options.limit, 200));
14862
16581
  const replay = buildWorkGraphHookReplayPatch(readResult);
14863
16582
  if (replay.records === 0) {
@@ -14889,10 +16608,10 @@ async function runHookReplayCommand(options) {
14889
16608
  }
14890
16609
  function readAuditInput(options, interactive) {
14891
16610
  if (options.input?.trim()) {
14892
- return readFileSync8(resolve3(options.input.trim()), "utf8");
16611
+ return readFileSync10(resolve4(options.input.trim()), "utf8");
14893
16612
  }
14894
16613
  if (!process.stdin.isTTY) {
14895
- return readFileSync8(0, "utf8");
16614
+ return readFileSync10(0, "utf8");
14896
16615
  }
14897
16616
  if (!interactive) {
14898
16617
  throw new Error("Audit input is required. Pass --input <file> or pipe text into wizard audit.");
@@ -14924,8 +16643,8 @@ function collectPathOption(value, previous = []) {
14924
16643
  ];
14925
16644
  }
14926
16645
  function parseClientExtractionFile(path) {
14927
- const resolvedPath = resolve3(path);
14928
- const parsed = JSON.parse(readFileSync8(resolvedPath, "utf8"));
16646
+ const resolvedPath = resolve4(path);
16647
+ const parsed = JSON.parse(readFileSync10(resolvedPath, "utf8"));
14929
16648
  if (!isRecord(parsed)) {
14930
16649
  throw new Error(`AI-client extraction must be a JSON object: ${resolvedPath}`);
14931
16650
  }
@@ -14947,8 +16666,8 @@ async function readAuditImports(options, interactive) {
14947
16666
  const missingSources = [];
14948
16667
  if (sources.length > 0) {
14949
16668
  const imported = loadAiSessionImports({
14950
- ...options.claudeProjectsDir?.trim() ? { claudeProjectsDir: resolve3(options.claudeProjectsDir.trim()) } : {},
14951
- ...options.codexSessionsDir?.trim() ? { codexSessionsDir: resolve3(options.codexSessionsDir.trim()) } : {},
16669
+ ...options.claudeProjectsDir?.trim() ? { claudeProjectsDir: resolve4(options.claudeProjectsDir.trim()) } : {},
16670
+ ...options.codexSessionsDir?.trim() ? { codexSessionsDir: resolve4(options.codexSessionsDir.trim()) } : {},
14952
16671
  limitPerSource: parsePositiveInteger(options.sessionLimit, 3, "--session-limit"),
14953
16672
  sinceDays: parsePositiveInteger(options.sessionDays, 30, "--session-days"),
14954
16673
  sources
@@ -14988,8 +16707,8 @@ async function readWorkGraphInputs(options, interactive) {
14988
16707
  const clientExtractions = readClientExtractions(options);
14989
16708
  const investigationSources = parseInvestigationSourceList(options.from);
14990
16709
  const investigationSourceData = investigationSources.length > 0 ? loadWorkGraphInvestigationSourceData({
14991
- ...options.claudeProjectsDir?.trim() ? { claudeProjectsDir: resolve3(options.claudeProjectsDir.trim()) } : {},
14992
- ...options.codexSessionsDir?.trim() ? { codexSessionsDir: resolve3(options.codexSessionsDir.trim()) } : {},
16710
+ ...options.claudeProjectsDir?.trim() ? { claudeProjectsDir: resolve4(options.claudeProjectsDir.trim()) } : {},
16711
+ ...options.codexSessionsDir?.trim() ? { codexSessionsDir: resolve4(options.codexSessionsDir.trim()) } : {},
14993
16712
  cwd: process.cwd(),
14994
16713
  limitPerSource: parsePositiveInteger(options.sessionLimit, 8, "--session-limit"),
14995
16714
  sinceDays: parsePositiveInteger(options.sessionDays, 45, "--session-days"),
@@ -15106,10 +16825,10 @@ async function runAuditCommand(options) {
15106
16825
  workspace
15107
16826
  });
15108
16827
  const markdown = renderSelfAuditMarkdown(plan);
15109
- const outputDir = resolve3(options.outputDir?.trim() || ".orgx/audits");
16828
+ const outputDir = resolve4(options.outputDir?.trim() || ".orgx/audits");
15110
16829
  const timestamp = plan.generated_at.replace(/[:.]/g, "-");
15111
- const jsonPath = resolve3(outputDir, `ai-native-self-audit-${timestamp}.json`);
15112
- const markdownPath = resolve3(outputDir, `ai-native-self-audit-${timestamp}.md`);
16830
+ const jsonPath = resolve4(outputDir, `ai-native-self-audit-${timestamp}.json`);
16831
+ const markdownPath = resolve4(outputDir, `ai-native-self-audit-${timestamp}.md`);
15113
16832
  writeJsonFile(jsonPath, plan);
15114
16833
  writeTextFile(markdownPath, markdown);
15115
16834
  if (options.json) {
@@ -15240,7 +16959,7 @@ async function runOperatingMapCommand(queryParts, options) {
15240
16959
  }
15241
16960
  function runWorkGraphExtractionSchemaCommand(options) {
15242
16961
  const protocol = buildWorkGraphExtractionProtocol();
15243
- const outputPath = options.output?.trim() ? resolve3(options.output.trim()) : "";
16962
+ const outputPath = options.output?.trim() ? resolve4(options.output.trim()) : "";
15244
16963
  if (outputPath) {
15245
16964
  if (options.json) {
15246
16965
  writeJsonFile(outputPath, protocol);
@@ -15270,8 +16989,8 @@ function normalizeRuntimePacketRole(role) {
15270
16989
  }
15271
16990
  function runWorkGraphRuntimeEventCommand(options) {
15272
16991
  const source = normalizeRuntimePacketSource(options.source);
15273
- const cwd = resolve3(options.cwd?.trim() || process.cwd());
15274
- const outputRoot = resolve3(cwd, options.outputDir?.trim() || ".orgx/work-graph/runtime-events");
16992
+ const cwd = resolve4(options.cwd?.trim() || process.cwd());
16993
+ const outputRoot = resolve4(cwd, options.outputDir?.trim() || ".orgx/work-graph/runtime-events");
15275
16994
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
15276
16995
  const timestamp = generatedAt.replace(/[:.]/g, "-");
15277
16996
  const summary = options.summary?.trim() || options.message?.trim();
@@ -15292,7 +17011,7 @@ function runWorkGraphRuntimeEventCommand(options) {
15292
17011
  collection_method: "runtime_packet",
15293
17012
  redaction_state: "agent_redacted"
15294
17013
  };
15295
- const path = resolve3(outputRoot, source, `${timestamp}-${process.pid}.jsonl`);
17014
+ const path = resolve4(outputRoot, source, `${timestamp}-${process.pid}.jsonl`);
15296
17015
  writeTextFile(path, `${JSON.stringify(packet)}
15297
17016
  `, { mode: 384 });
15298
17017
  if (options.json) {
@@ -15335,11 +17054,11 @@ async function runWorkGraphCommand(options, defaults = {}) {
15335
17054
  workspace
15336
17055
  });
15337
17056
  const markdown = renderWorkGraphMarkdown(report);
15338
- const outputDir = resolve3(commandOptions.outputDir?.trim() || ".orgx/work-graph");
17057
+ const outputDir = resolve4(commandOptions.outputDir?.trim() || ".orgx/work-graph");
15339
17058
  const timestamp = report.generated_at.replace(/[:.]/g, "-");
15340
- const jsonPath = resolve3(outputDir, `work-graph-report-${timestamp}.json`);
15341
- const markdownPath = resolve3(outputDir, `work-graph-report-${timestamp}.md`);
15342
- const agentBriefPath = resolve3(outputDir, `work-graph-agent-brief-${timestamp}.md`);
17059
+ const jsonPath = resolve4(outputDir, `work-graph-report-${timestamp}.json`);
17060
+ const markdownPath = resolve4(outputDir, `work-graph-report-${timestamp}.md`);
17061
+ const agentBriefPath = resolve4(outputDir, `work-graph-agent-brief-${timestamp}.md`);
15343
17062
  writeJsonFile(jsonPath, report);
15344
17063
  writeTextFile(markdownPath, markdown);
15345
17064
  let published = null;
@@ -15754,14 +17473,14 @@ async function readSingleKey() {
15754
17473
  const stdin = process.stdin;
15755
17474
  if (!stdin.isTTY) return null;
15756
17475
  const previousRawMode = stdin.isRaw === true;
15757
- return await new Promise((resolve4) => {
17476
+ return await new Promise((resolve5) => {
15758
17477
  const cleanup = (result) => {
15759
17478
  stdin.off("data", onData);
15760
17479
  if (stdin.isTTY) {
15761
17480
  stdin.setRawMode(previousRawMode);
15762
17481
  }
15763
17482
  stdin.pause();
15764
- resolve4(result);
17483
+ resolve5(result);
15765
17484
  };
15766
17485
  const onData = (chunk) => {
15767
17486
  const text2 = chunk.toString("utf8");
@@ -16526,7 +18245,7 @@ async function main() {
16526
18245
  initializeWizardSentry();
16527
18246
  const program = new Command();
16528
18247
  program.name("orgx-wizard").description("Add OrgX MCP configs, skills/rules, and companion plugins to your local AI tools.").showHelpAfterError();
16529
- const pkgVersion = true ? "0.1.56" : void 0;
18248
+ const pkgVersion = true ? "0.1.57" : void 0;
16530
18249
  program.version(pkgVersion ?? "unknown", "-V, --version");
16531
18250
  program.hook("preAction", (_thisCommand, actionCommand) => {
16532
18251
  if (Boolean(actionCommand.optsWithGlobals().json)) return;
@@ -17326,9 +19045,15 @@ async function main() {
17326
19045
  }
17327
19046
  }
17328
19047
  });
19048
+ hooks.command("flush").description("Deliver queued session summaries and retain every unacknowledged capture.").option("--queue <path>", "session-summary capture queue path").option("--limit <count>", "maximum queued captures to attempt", "100").option("--background", "run quietly as a hook-triggered delivery worker").option("--json", "emit a JSON command summary").action(async (options) => {
19049
+ await runSessionSummaryFlushCommand(options);
19050
+ });
17329
19051
  hooks.command("replay").description("Replay passive hook outbox events into a claimed Work Graph profile.").requiredOption("--fingerprint <fingerprint>", "target Work Graph fingerprint, for example wgf_0123...").option("--outbox <path>", "runtime hook JSONL outbox path").option("--limit <count>", "maximum recent hook events to replay", "200").option("--yes", "approve publishing hook evidence without prompting").option("--json", "emit a JSON command summary").action(async (options) => {
17330
19052
  await runHookReplayCommand(options);
17331
19053
  });
19054
+ hooks.command("backfill").description("Distill the historical hook spool into lean session summaries (preview by default).").option("--spool <path>", "runtime hook JSONL spool path").option("--post", "upload the distilled summaries to OrgX (requires --yes)").option("--truncate", "archive and empty the spool after a fully successful --post (requires --yes)").option("--yes", "confirm uploading or truncating your local work history").option("--json", "emit a JSON command summary").action(async (options) => {
19055
+ await runHookBackfillCommand(options);
19056
+ });
17332
19057
  program.command("doctor").description("Verify OrgX health, or diagnose a credential-free workload shape.").option("--workload <path>", "diagnose a sanitized workload-shape JSON file; use - for stdin").option("--base-url <url>", "explicit workload endpoint override for testing or self-hosting").option("--json", "emit the selected doctor result as JSON").action(async (options) => {
17333
19058
  if (options.baseUrl && !options.workload) {
17334
19059
  const message = "--base-url is only valid together with --workload.";
@@ -17564,4 +19289,4 @@ main().catch(async (error) => {
17564
19289
  process.exitCode = 1;
17565
19290
  });
17566
19291
  //# sourceMappingURL=cli.js.map
17567
- //# debugId=02842523-d681-5cea-96aa-0f0c896e9fb5
19292
+ //# debugId=8eca99e4-958f-5704-a1cb-bd96bbd10293