@zhivex-ai/harness 1.2.0-rc.8 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +7 -7
  3. package/contracts/public-api.json +5 -0
  4. package/contracts/stable-api-signatures.json +28 -28
  5. package/dist/acp-cli.js +1096 -609
  6. package/dist/cli/arguments.d.ts +2 -0
  7. package/dist/cli/cli-credentials.d.ts +1 -1
  8. package/dist/cli/cli-options.d.ts +1 -1
  9. package/dist/cli/cli-profiles.d.ts +15 -0
  10. package/dist/cli/cli-stream.d.ts +3 -3
  11. package/dist/cli/console/console-commands.d.ts +3 -3
  12. package/dist/cli/console/console-input.d.ts +22 -3
  13. package/dist/cli/console/console-navigation.d.ts +3 -0
  14. package/dist/cli/console/console-presentation.d.ts +8 -2
  15. package/dist/cli/console/console-reasoning.d.ts +3 -0
  16. package/dist/cli/presentation.d.ts +3 -0
  17. package/dist/cli/run-document.d.ts +4 -4
  18. package/dist/cli/terminal/activity-history.d.ts +15 -0
  19. package/dist/cli/terminal/tool-activity.d.ts +11 -1
  20. package/dist/{cli-7nj89dq0.js → cli-3jmy4h5h.js} +385 -217
  21. package/dist/{cli-entry-t3bay5k5.js → cli-entry-427869g9.js} +287 -180
  22. package/dist/{cli-entry-0mhbpe8j.js → cli-entry-b1mfvmxe.js} +2 -1
  23. package/dist/{cli-entry-hkxakkng.js → cli-entry-c2f58zj2.js} +998 -210
  24. package/dist/cli.js +2 -2
  25. package/dist/client/json-contracts.d.ts +20 -0
  26. package/dist/{config-w1npe8mp.js → config-1k9t4jhh.js} +1 -1
  27. package/dist/context/compaction.d.ts +1 -1
  28. package/dist/context/semantic-compaction.d.ts +1 -1
  29. package/dist/context/task-memory.d.ts +2 -2
  30. package/dist/hostile-repository-demo.js +950 -509
  31. package/dist/index.js +1803 -1132
  32. package/dist/persistence/operations.d.ts +5 -1
  33. package/dist/persistence/state-backup-stream.d.ts +4 -0
  34. package/dist/providers/meta-replay.d.ts +1 -1
  35. package/dist/providers/qwen-context.d.ts +2 -0
  36. package/dist/providers/qwen-hosted-policy.d.ts +5 -0
  37. package/dist/providers/reasoning.d.ts +18 -0
  38. package/dist/runtime/config.d.ts +3 -0
  39. package/dist/runtime/error-diagnostics.d.ts +2 -0
  40. package/dist/runtime/harness.d.ts +1 -1
  41. package/dist/runtime/model-edit-references.d.ts +4 -2
  42. package/dist/runtime/progress-monitor.d.ts +6 -2
  43. package/dist/runtime/runtime-policy.d.ts +5 -4
  44. package/dist/runtime/time-to-safe-fix.d.ts +6 -1
  45. package/dist/service-cli.js +1112 -639
  46. package/dist/{service-client-cli-s3ee2hrj.js → service-client-cli-yx56n0hc.js} +66 -8
  47. package/dist/tools/dependency-read.d.ts +51 -4
  48. package/dist/tools/execution.d.ts +8 -8
  49. package/dist/tools/workspace.d.ts +27 -16
  50. package/dist/verify-historical-migrations.js +1122 -638
  51. package/dist/workspace/file-security.d.ts +6 -0
  52. package/dist/zhx.js +2 -2
  53. package/docs/CLI.md +75 -23
  54. package/docs/CONTEXT_ENGINEERING.md +25 -5
  55. package/docs/DURABLE_OPERATIONS.md +8 -0
  56. package/docs/EXTENSIBILITY.md +1 -1
  57. package/docs/FIRST_USE.md +6 -6
  58. package/docs/MODEL_CATALOG.md +28 -1
  59. package/evaluations/representative-assembly-matrix.json +55 -1
  60. package/evaluations/representative-repositories-v2.jsonl +7 -0
  61. package/examples/README.md +4 -4
  62. package/package.json +7 -7
@@ -5,8 +5,9 @@ import {
5
5
  resolveHelpTopic,
6
6
  shortCliHelp,
7
7
  validateCliCommandOptions
8
- } from "./cli-entry-0mhbpe8j.js";
8
+ } from "./cli-entry-b1mfvmxe.js";
9
9
  import {
10
+ ActivityHistory,
10
11
  CLI_JSON_SCHEMA_VERSION,
11
12
  CliOciRuntimeAdapter,
12
13
  ConsoleInput,
@@ -20,6 +21,7 @@ import {
20
21
  MAX_HARNESS_CONTEXT_TOTAL_BYTES,
21
22
  SqliteDatabase,
22
23
  TASK_SOURCE_KEY,
24
+ ToolActivity,
23
25
  USAGE_LEDGER_KEY,
24
26
  UnsafeFileTypeError,
25
27
  Workspace,
@@ -27,16 +29,18 @@ import {
27
29
  cancelHarnessRun,
28
30
  canonicalizeChangeEnvelope,
29
31
  changeEnvelopePreconditionsSchema,
32
+ chooseReasoning,
30
33
  cleanupHarnessExecutionArtifacts,
31
34
  cleanupHarnessRuns,
32
35
  compactHarnessMessages,
36
+ compactToolResult,
33
37
  createChangeEnvelope,
34
38
  createHarness,
35
39
  digestChangeEnvelopeArtifact,
36
40
  estimateMessages,
37
41
  fileDigestSchema,
42
+ formatAppliedFiles,
38
43
  formatApproval,
39
- formatComposer,
40
44
  formatConsoleHelp,
41
45
  formatConsoleWelcome,
42
46
  formatTerminalEvent,
@@ -53,6 +57,7 @@ import {
53
57
  navigateConsole,
54
58
  openCliSessionStore,
55
59
  openHarnessPersistence,
60
+ readRegularFileChunksNoFollow,
56
61
  readRegularFileNoFollow,
57
62
  resolvePackageManager,
58
63
  resolveTerminalApprovals,
@@ -63,6 +68,7 @@ import {
63
68
  sanitizeTerminalText,
64
69
  serializeStreamEvent,
65
70
  serializeStreamResult,
71
+ sqliteAdapter,
66
72
  taskSources,
67
73
  terminalContinuationMessages,
68
74
  terminalRunFailure,
@@ -71,7 +77,7 @@ import {
71
77
  validateStateDirectory,
72
78
  verifyChangeEnvelope,
73
79
  workspaceFilePathSchema
74
- } from "./cli-entry-hkxakkng.js";
80
+ } from "./cli-entry-c2f58zj2.js";
75
81
  import {
76
82
  BUN_ENGINE_RANGE,
77
83
  HARNESS_VERSION,
@@ -89,12 +95,14 @@ import {
89
95
  PROVIDERS,
90
96
  createProviderModel,
91
97
  harnessErrorDocument,
98
+ modelReasoningEfforts,
92
99
  normalizeHarnessError,
93
100
  parseProvider,
94
101
  providerAvailability,
95
102
  providerDescriptor,
103
+ reasoningEffortSchema,
96
104
  resolveHarnessConfig
97
- } from "./cli-entry-t3bay5k5.js";
105
+ } from "./cli-entry-427869g9.js";
98
106
  import {
99
107
  __require
100
108
  } from "./cli-entry-1bx83sw4.js";
@@ -117,6 +125,7 @@ var profileNamePattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
117
125
  var cliProfileSchema = z.strictObject({
118
126
  schemaVersion: z.literal(CLI_PROFILE_SCHEMA_VERSION),
119
127
  provider: z.enum(PROVIDERS),
128
+ reasoningEffort: reasoningEffortSchema.optional(),
120
129
  model: z.string().min(1).max(512).refine((value) => !/[\u0000-\u001f\u007f]/.test(value), "model must contain printable characters")
121
130
  });
122
131
  var absoluteDirectory = (value, source, pathApi = path) => {
@@ -224,7 +233,8 @@ var createCliProfile = async (name, input, context = {}) => {
224
233
  const parsed = cliProfileSchema.safeParse({
225
234
  schemaVersion: CLI_PROFILE_SCHEMA_VERSION,
226
235
  provider: input.provider,
227
- model: input.model
236
+ model: input.model,
237
+ ...input.reasoningEffort ? { reasoningEffort: input.reasoningEffort } : {}
228
238
  });
229
239
  if (!parsed.success) {
230
240
  throw new HarnessConfigError(`CLI profile is invalid: ${parsed.error.issues[0]?.message ?? "schema mismatch"}`);
@@ -287,6 +297,7 @@ var applyCliProfile = async (options, context = {}) => {
287
297
  return {
288
298
  provider: profile.provider,
289
299
  model: profile.model,
300
+ ...profile.reasoningEffort && (!options.provider || options.provider === profile.provider) && (!options.model || options.model === profile.model) ? { reasoningEffort: profile.reasoningEffort } : {},
290
301
  ...options
291
302
  };
292
303
  };
@@ -545,6 +556,10 @@ var parseCliArgs = (argv) => {
545
556
  index += 1;
546
557
  break;
547
558
  }
559
+ case "--reasoning":
560
+ options.reasoningEffort = reasoningEffortSchema.parse(optionValue(argv, index, argument));
561
+ index += 1;
562
+ break;
548
563
  case "--require-verified-delivery":
549
564
  options.requireVerifiedDelivery = true;
550
565
  break;
@@ -1018,103 +1033,6 @@ var parseCliArgs = (argv) => {
1018
1033
  return options;
1019
1034
  };
1020
1035
 
1021
- // src/cli/terminal/tool-activity.ts
1022
- var labels = {
1023
- read_file: "Reading a file",
1024
- read_files: "Reading files",
1025
- read_dependency: "Inspecting dependency types",
1026
- search_files: "Searching files",
1027
- search_many: "Searching the project",
1028
- list_files: "Listing files",
1029
- run_check: "Running checks",
1030
- run_environment_command: "Running a command",
1031
- apply_patch: "Applying edits",
1032
- apply_reviewed_edits: "Applying reviewed edits",
1033
- apply_reviewed_replacement: "Applying a reviewed replacement",
1034
- git_diff: "Reviewing changes"
1035
- };
1036
- var summaries = {
1037
- read_file: "reads",
1038
- read_files: "reads",
1039
- read_dependency: "reads",
1040
- search_files: "searches",
1041
- search_many: "searches",
1042
- list_files: "listings",
1043
- apply_patch: "edits",
1044
- apply_reviewed_edits: "edits",
1045
- apply_reviewed_replacement: "edits",
1046
- git_diff: "diffs"
1047
- };
1048
-
1049
- class ToolActivity {
1050
- write;
1051
- tty;
1052
- columns;
1053
- active = 0;
1054
- visible = false;
1055
- label = "Working";
1056
- completed = new Map;
1057
- started = Date.now();
1058
- timer;
1059
- constructor(write, tty, columns = () => 80) {
1060
- this.write = write;
1061
- this.tty = tty;
1062
- this.columns = columns;
1063
- }
1064
- phase(label, _step) {
1065
- this.clear();
1066
- this.label = label;
1067
- this.begin();
1068
- }
1069
- start(name) {
1070
- this.active++;
1071
- this.label = labels[name ?? ""] ?? "Running a tool";
1072
- this.begin();
1073
- }
1074
- finish(name, success = true) {
1075
- this.active = Math.max(0, this.active - 1);
1076
- if (success && !["run_check", "run_environment_command"].includes(name) && !name.startsWith("verify_and_apply_")) {
1077
- const label = summaries[name] ?? "other operations";
1078
- this.completed.set(label, (this.completed.get(label) ?? 0) + 1);
1079
- }
1080
- this.label = this.active ? this.label : "Waiting for model response";
1081
- this.begin();
1082
- }
1083
- begin() {
1084
- this.started = Date.now();
1085
- this.render();
1086
- if (this.tty && !this.timer) {
1087
- this.timer = setInterval(() => this.render(), 250);
1088
- this.timer.unref();
1089
- }
1090
- }
1091
- render() {
1092
- if (!this.tty)
1093
- return;
1094
- const elapsed = Math.floor((Date.now() - this.started) / 1000);
1095
- const done = [...this.completed.values()].reduce((sum, count) => sum + count, 0);
1096
- const text = `… ${this.label}${elapsed ? ` · ${elapsed}s` : ""}${done ? ` · ${done} tools done` : ""}`;
1097
- this.write(`\r\x1B[2K${Array.from(text).slice(0, Math.max(1, this.columns() - 1)).join("")}`);
1098
- this.visible = true;
1099
- }
1100
- clear() {
1101
- clearInterval(this.timer);
1102
- this.timer = undefined;
1103
- if (this.visible)
1104
- this.write("\r\x1B[2K");
1105
- this.visible = false;
1106
- }
1107
- flush() {
1108
- this.clear();
1109
- if (this.completed.size) {
1110
- const details = [...this.completed].map(([label, count]) => `${label}: ${count}`).join(" · ");
1111
- this.write(`✓ Activity · ${details}
1112
- `);
1113
- }
1114
- this.completed.clear();
1115
- }
1116
- }
1117
-
1118
1036
  // src/cli/terminal/terminal-table.ts
1119
1037
  var segments = new Intl.Segmenter(undefined, { granularity: "grapheme" });
1120
1038
  var graphemes = (value) => [...segments.segment(value)].map((item) => item.segment);
@@ -1395,6 +1313,7 @@ var harnessConfigInput = (config) => ({
1395
1313
  schemaVersion: config.schemaVersion,
1396
1314
  provider: config.provider,
1397
1315
  model: config.model,
1316
+ ...config.reasoningEffort ? { reasoningEffort: config.reasoningEffort } : {},
1398
1317
  workspace: config.workspace,
1399
1318
  stateDirectory: config.stateDirectory,
1400
1319
  storeBackend: config.storeBackend,
@@ -1549,74 +1468,21 @@ var approvalResponses = (approvals, approve, reason) => approvals.map((approval)
1549
1468
  reason
1550
1469
  }));
1551
1470
  var terminalApprovalResolver = (automaticallyApprove, ask, ui) => {
1552
- const grants = new Set;
1553
1471
  return async (approvals) => {
1554
1472
  if (automaticallyApprove === "restricted")
1555
1473
  return approvalResponses(approvals, false, "Denied by restricted approval mode.");
1556
- if ((automaticallyApprove === true || automaticallyApprove === "auto") && !approvals.some((a) => a.name === "read_dependency")) {
1557
- return approvalResponses(approvals, true, "Approved by --yes.");
1474
+ if (automaticallyApprove === true || automaticallyApprove === "auto") {
1475
+ return approvalResponses(approvals, true, "Approved by automatic mode.");
1558
1476
  }
1559
- if (!ask && (!process.stdin.isTTY || !process.stdout.isTTY)) {
1477
+ if (!ask && (!process.stdin.isTTY || !process.stdout.isTTY))
1560
1478
  return;
1561
- }
1562
1479
  const readline = ask ? undefined : createInterface({ input: process.stdin, output: process.stdout });
1563
- const question = ask ?? ((text) => readline.question(text));
1564
1480
  try {
1565
- if (!approvals.some((a) => a.name === "read_dependency")) {
1566
- return await resolveTerminalApprovals(approvals, { ask: question, write: (text) => process.stderr.write(text), ...ui });
1567
- }
1568
- const responses = [];
1569
- const staged = new Set(grants);
1570
- const stagedSession = ui?.sessionGrants ? new Set(ui.sessionGrants) : undefined;
1571
- for (const approval of approvals) {
1572
- if (approval.name === "read_dependency") {
1573
- let name;
1574
- try {
1575
- const args = typeof approval.arguments === "string" ? JSON.parse(approval.arguments) : approval.arguments;
1576
- if (args && typeof args.package === "string" && /^(?:@[a-z0-9_-]+\/)?[a-z0-9][a-z0-9._-]*$/.test(args.package))
1577
- name = args.package;
1578
- } catch {}
1579
- const key = name ? JSON.stringify([approval.provider, approval.childAgentId ?? "", name]) : undefined;
1580
- const sessionKey = key && ui?.workspace ? JSON.stringify(["dependency", ui.workspace, key]) : undefined;
1581
- if (sessionKey && stagedSession?.has(sessionKey)) {
1582
- responses.push(...approvalResponses([approval], true, "Dependency metadata/types approved for this CLI session."));
1583
- continue;
1584
- }
1585
- if (key && staged.has(key)) {
1586
- responses.push(...approvalResponses([approval], true, "Dependency read approved for this task."));
1587
- continue;
1588
- }
1589
- process.stderr.write(formatApproval(approval, { detail: "summary" }) + `
1590
- `);
1591
- const answer = ui ? await ui.select(`Dependency metadata/types: ${name ?? "invalid package"}`, [
1592
- { value: "n", label: "Reject" },
1593
- { value: "y", label: "Allow once" },
1594
- { value: "t", label: "Allow this package for this task", detail: "Metadata and type declarations only" },
1595
- ...stagedSession ? [{ value: "s", label: "Allow this package for this session", detail: "Metadata and type declarations only" }] : [],
1596
- { value: "q", label: "Leave pending" }
1597
- ]) ?? "q" : (await question(`Read dependency ${name ?? "(invalid package)"} metadata/types? [y] once / [t] this task / [n] deny / [q] leave pending: `)).trim().toLowerCase();
1598
- if (answer === "q")
1599
- return;
1600
- const approved = Boolean(key && (["y", "yes", "t"].includes(answer) || answer === "s" && stagedSession && sessionKey));
1601
- if (approved && answer === "t")
1602
- staged.add(key);
1603
- if (approved && answer === "s" && sessionKey)
1604
- stagedSession?.add(sessionKey);
1605
- responses.push(...approvalResponses([approval], approved, approved ? "Approved bounded dependency read." : "Dependency read denied."));
1606
- } else if (automaticallyApprove === true || automaticallyApprove === "auto") {
1607
- responses.push(...approvalResponses([approval], true, "Approved by automatic mode."));
1608
- } else {
1609
- const result = await resolveTerminalApprovals([approval], { ask: question, write: (text) => process.stderr.write(text), ...ui, ...stagedSession ? { sessionGrants: stagedSession } : {} });
1610
- if (!result)
1611
- return;
1612
- responses.push(...result);
1613
- }
1614
- }
1615
- for (const key of staged)
1616
- grants.add(key);
1617
- for (const key of stagedSession ?? [])
1618
- ui?.sessionGrants?.add(key);
1619
- return responses;
1481
+ return await resolveTerminalApprovals(approvals, {
1482
+ ask: ask ?? ((text) => readline.question(text)),
1483
+ write: (text) => process.stderr.write(text),
1484
+ ...ui
1485
+ });
1620
1486
  } finally {
1621
1487
  readline?.close();
1622
1488
  }
@@ -1668,6 +1534,8 @@ var toolActivities = new WeakMap;
1668
1534
  var lastTerminalFailures = new WeakMap;
1669
1535
  var flushToolActivity = (tracker) => toolActivities.get(tracker)?.flush();
1670
1536
  var streamSink = (output, tracker, compact = false) => async (event) => {
1537
+ if (!output.json && !output.jsonl)
1538
+ tracker.activityHistory?.observe(event);
1671
1539
  if (output.jsonl) {
1672
1540
  tracker.sequence = (tracker.sequence ?? 0) + 1;
1673
1541
  process.stdout.write(`${serializeStreamEvent(event, tracker.sequence)}
@@ -1681,7 +1549,7 @@ var streamSink = (output, tracker, compact = false) => async (event) => {
1681
1549
  if (!activity) {
1682
1550
  activity = new ToolActivity((text) => {
1683
1551
  process.stderr.write(text);
1684
- }, Boolean(process.stderr.isTTY && process.stdout.isTTY), () => process.stderr.columns || 80);
1552
+ }, Boolean(process.stderr.isTTY && process.stdout.isTTY), () => process.stderr.columns || 80, tracker.inputStatus);
1685
1553
  toolActivities.set(tracker, activity);
1686
1554
  }
1687
1555
  if (event.type === "agent-compaction") {
@@ -1721,16 +1589,40 @@ var streamSink = (output, tracker, compact = false) => async (event) => {
1721
1589
  }
1722
1590
  if (event.type === "tool-result") {
1723
1591
  activity.finish(event.toolResult.toolName, !event.toolResult.isError);
1724
- if (!event.toolResult.isError && !["run_check", "run_environment_command"].includes(event.toolResult.toolName) && !event.toolResult.toolName.startsWith("verify_and_apply_"))
1592
+ const detail = compactToolResult(event.toolResult);
1593
+ const check = ["run_check", "run_environment_command"].includes(event.toolResult.toolName) || event.toolResult.toolName.startsWith("verify_and_apply_");
1594
+ if (detail && !check) {
1595
+ activity.pause();
1596
+ tracker.markdown?.flush();
1597
+ if (proseLineOpen.has(tracker)) {
1598
+ process.stdout.write(`
1599
+ `);
1600
+ proseLineOpen.delete(tracker);
1601
+ }
1602
+ process.stderr.write(`${detail}
1603
+ `);
1604
+ return;
1605
+ }
1606
+ if (!check)
1725
1607
  return;
1608
+ activity.pause();
1609
+ } else if (["agent-run-finish", "error", "tool-approval-request", "agent-approval-request"].includes(event.type)) {
1610
+ tracker.markdown?.flush();
1611
+ if (proseLineOpen.has(tracker)) {
1612
+ process.stdout.write(`
1613
+ `);
1614
+ proseLineOpen.delete(tracker);
1615
+ }
1726
1616
  activity.flush();
1727
1617
  } else if (!["provider-data", "finish", "agent-step-start", "agent-step-finish"].includes(event.type)) {
1728
- activity.flush();
1618
+ activity.pause();
1729
1619
  }
1730
1620
  }
1731
1621
  if (!output.json && event.type === "text-delta") {
1732
1622
  if (compact && !conversationStarted.has(tracker)) {
1733
- process.stdout.write(`
1623
+ process.stdout.write(terminalSupportsColor(Boolean(process.stdout.isTTY)) ? `
1624
+ \x1B[1mZhivex\x1B[0m
1625
+ ` : `
1734
1626
  Zhivex
1735
1627
  `);
1736
1628
  conversationStarted.add(tracker);
@@ -1757,9 +1649,12 @@ Zhivex
1757
1649
  return;
1758
1650
  tracker.markdown?.flush();
1759
1651
  proseLineOpen.delete(tracker);
1760
- const line = formatTerminalEvent(event, {
1652
+ let line = formatTerminalEvent(event, {
1761
1653
  color: terminalSupportsColor(Boolean(process.stderr.isTTY))
1762
1654
  });
1655
+ if (compact && event.type === "tool-result" && event.toolResult.isError && line?.endsWith(" · error")) {
1656
+ line = compactToolResult(event.toolResult);
1657
+ }
1763
1658
  if ((event.type === "error" || event.type === "agent-run-finish" && event.status === "failed") && line) {
1764
1659
  const cause = terminalRunFailure(event.type === "error" ? event.error : event.state.error);
1765
1660
  if (lastTerminalFailures.get(tracker) === cause)
@@ -1803,7 +1698,10 @@ var initializeCli = async (options, context = {}) => {
1803
1698
  } catch (error) {
1804
1699
  throw new CliUsageError(error instanceof Error ? error.message : String(error));
1805
1700
  }
1701
+ let reasoningEffort = options.reasoningEffort ?? existing?.reasoningEffort;
1806
1702
  let model = options.model ?? (existing ? existing.provider === provider ? existing.model : providerDescriptor(provider).defaultModel : process.env.ZHIVEX_HARNESS_MODEL ?? providerDescriptor(provider).defaultModel);
1703
+ if (existing && (existing.provider !== provider || existing.model !== model) && options.reasoningEffort === undefined)
1704
+ reasoningEffort = undefined;
1807
1705
  if (!options.json && process.stdin.isTTY && process.stdout.isTTY) {
1808
1706
  const input = new ConsoleInput(process.stdin, process.stdout);
1809
1707
  try {
@@ -1811,7 +1709,7 @@ var initializeCli = async (options, context = {}) => {
1811
1709
  const providers = [...providerAvailability()].sort((a, b) => Number(b.configured) - Number(a.configured) || Number(b.id === provider) - Number(a.id === provider));
1812
1710
  const selected = await navigateConsole(input, {
1813
1711
  entry: options.provider ? "model" : "provider",
1814
- current: { provider, model },
1712
+ current: { provider, model, ...reasoningEffort ? { reasoningEffort } : {} },
1815
1713
  providers,
1816
1714
  sessions: async () => []
1817
1715
  });
@@ -1822,12 +1720,16 @@ var initializeCli = async (options, context = {}) => {
1822
1720
  }
1823
1721
  provider = parseProvider(selected.provider);
1824
1722
  model = options.model ?? selected.model;
1723
+ reasoningEffort = options.reasoningEffort ?? selected.reasoningEffort;
1825
1724
  }
1826
1725
  } finally {
1827
1726
  input.close();
1828
1727
  }
1829
1728
  }
1830
- const created = await (options.updateProfile ? updateCliProfile : createCliProfile)(profileName, { provider, model });
1729
+ if (reasoningEffort && !modelReasoningEfforts(provider, model).includes(reasoningEffort)) {
1730
+ throw new CliUsageError(`Reasoning effort ${reasoningEffort} is not declared for ${provider}/${model}. Use default or a supported level.`);
1731
+ }
1732
+ const created = await (options.updateProfile ? updateCliProfile : createCliProfile)(profileName, { provider, model, ...reasoningEffort ? { reasoningEffort } : {} });
1831
1733
  const availability = providerAvailability().find((candidate) => candidate.id === provider);
1832
1734
  const doctorCommand = `zhx doctor --profile ${profileName}`;
1833
1735
  const runCommand = `zhx --profile ${profileName} "inspect this repository"`;
@@ -1839,7 +1741,8 @@ var initializeCli = async (options, context = {}) => {
1839
1741
  path: created.path,
1840
1742
  schemaVersion: created.profile.schemaVersion,
1841
1743
  provider: created.profile.provider,
1842
- model: created.profile.model
1744
+ model: created.profile.model,
1745
+ reasoningEffort: created.profile.reasoningEffort ?? "default"
1843
1746
  },
1844
1747
  credential: {
1845
1748
  configured: availability?.configured ?? false,
@@ -1853,18 +1756,18 @@ var initializeCli = async (options, context = {}) => {
1853
1756
  return true;
1854
1757
  }
1855
1758
  if (context.onboarding) {
1856
- process.stdout.write(`Provider and model saved. Next: configure credentials for ${provider}.
1759
+ process.stdout.write(`Provider and model saved. Reasoning: ${created.profile.reasoningEffort ?? "default"}. Next: configure credentials for ${provider}.
1857
1760
  `);
1858
1761
  return true;
1859
1762
  }
1860
1763
  process.stdout.write([
1861
1764
  `${options.updateProfile ? "Updated" : "Created"} personal profile ${profileName} at ${created.path}.`,
1862
- `Provider: ${created.profile.provider} · Model: ${created.profile.model}`,
1765
+ `Provider: ${created.profile.provider} · Model: ${created.profile.model} · Reasoning: ${created.profile.reasoningEffort ?? "default"}`,
1863
1766
  availability?.configured ? "Provider credential detected; no secret value was stored or printed." : `Open zhx to configure a managed key, or set ${availability?.credentialNames.join(" or ") || "the provider credential"} for automation.`,
1864
1767
  "Next:",
1865
1768
  ` ${doctorCommand}`,
1866
1769
  ` ${runCommand}`,
1867
- "Profiles contain provider and model only. A later bare interactive console uses the default profile automatically. Change it with zhx init --update."
1770
+ "Profiles contain provider, model and optional reasoning effort. A later bare interactive console uses the default profile automatically. Change it with zhx init --update."
1868
1771
  ].join(`
1869
1772
  `) + `
1870
1773
  `);
@@ -1918,6 +1821,7 @@ Options (automation and advanced configuration):
1918
1821
  --update Update an existing profile (init only)
1919
1822
  --provider <${PROVIDERS.join("|")}> Provider (default: openai)
1920
1823
  --model <id> Override the default model
1824
+ --reasoning <level> default, none, minimal, low, medium, high, xhigh, max (model-dependent)
1921
1825
  --compaction-model <id> Opt into model-assisted compaction with this model
1922
1826
  --compaction-provider <id> Compaction provider (defaults to the primary provider)
1923
1827
  --route <role=provider[:model]> Route a subagent role; repeatable
@@ -2323,7 +2227,7 @@ This connection will replace Qwen environment settings for this CLI session.
2323
2227
  }
2324
2228
  }
2325
2229
  async function credentialModel(config, env, modelFactory) {
2326
- const { createProviderModel: createProviderModel2 } = await import("./config-w1npe8mp.js");
2230
+ const { createProviderModel: createProviderModel2 } = await import("./config-1k9t4jhh.js");
2327
2231
  const { wrapLanguageModel } = await import("@zhivex-ai/core");
2328
2232
  const secrets = providerDescriptor(config.provider).credentialNames.map((name) => env[name]).filter((value) => !!value);
2329
2233
  const safeError = (error) => {
@@ -2936,6 +2840,7 @@ var consoleBudgetOptions = (options, env = process.env) => ({
2936
2840
  var restoreConsoleOptions = (current, saved) => ({
2937
2841
  ...current,
2938
2842
  ...saved,
2843
+ reasoningEffort: saved.reasoningEffort ?? "default",
2939
2844
  unlimitedTokens: saved.unlimitedTokens ?? false
2940
2845
  });
2941
2846
  var formatConsoleBudget = (config, usage) => {
@@ -3098,8 +3003,8 @@ var manageSessions = async (options) => {
3098
3003
  let document;
3099
3004
  switch (options.sessionsCommand) {
3100
3005
  case "list": {
3101
- const summaries2 = await store.list({ ...options.limit ? { limit: options.limit } : {}, ...options.sessionSearch ? { search: options.sessionSearch } : {} });
3102
- const indexed = (await Promise.all(summaries2.map((summary) => store.get(summary.sessionId)))).filter((value) => Boolean(value));
3006
+ const summaries = await store.list({ ...options.limit ? { limit: options.limit } : {}, ...options.sessionSearch ? { search: options.sessionSearch } : {} });
3007
+ const indexed = (await Promise.all(summaries.map((summary) => store.get(summary.sessionId)))).filter((value) => Boolean(value));
3103
3008
  const sessions = [];
3104
3009
  for (const selected of indexed)
3105
3010
  sessions.push(await refreshDurableStatus(selected));
@@ -3441,6 +3346,7 @@ var chat = async (options) => {
3441
3346
  const sessionStore = await openSessionStoreForConfig(baseConfig);
3442
3347
  const readline = new ConsoleInput(process.stdin, process.stdout);
3443
3348
  const attachments = new ConsoleAttachments;
3349
+ const activityHistory = new ActivityHistory;
3444
3350
  const credentials = { store: new CliCredentials, input: readline };
3445
3351
  let credentialsRevision = credentials.store.revision;
3446
3352
  let activeController;
@@ -3457,9 +3363,11 @@ Stopping the active operation; waiting for durable state and cleanup.
3457
3363
  const abortable = async (operation) => {
3458
3364
  const controller = new AbortController;
3459
3365
  activeController = controller;
3366
+ readline.startBackground();
3460
3367
  try {
3461
3368
  return await operation(controller.signal);
3462
3369
  } finally {
3370
+ readline.stopBackground();
3463
3371
  activeController = undefined;
3464
3372
  }
3465
3373
  };
@@ -3516,6 +3424,37 @@ Stopping the active operation; waiting for durable state and cleanup.
3516
3424
  sessionStore.close();
3517
3425
  throw error;
3518
3426
  }
3427
+ const consoleApprovals = async (approvals, context) => {
3428
+ readline.stopBackground();
3429
+ try {
3430
+ return await terminalApprovalResolver(options.approvalMode ?? options.yes, (question) => readline.question(question), { select: (title, items) => readline.select(title, items), workspace: harness.config.workspace, sessionGrants })(approvals, context);
3431
+ } finally {
3432
+ if (activeController && !activeController.signal.aborted)
3433
+ readline.startBackground();
3434
+ }
3435
+ };
3436
+ const createTracker = () => {
3437
+ let offset = harness.workspace.mutationAudit().length;
3438
+ const tracker = {
3439
+ streamedText: false,
3440
+ activityHistory,
3441
+ inputStatus: () => readline.backgroundStatus
3442
+ };
3443
+ return { tracker, onEvent: async (event) => {
3444
+ await streamSink({ json: false, jsonl: false }, tracker, !verbose)(event);
3445
+ if (event.type === "tool-result") {
3446
+ const audit = harness.workspace.mutationAudit();
3447
+ const receipt = formatAppliedFiles(audit.slice(offset));
3448
+ offset = audit.length;
3449
+ if (receipt) {
3450
+ flushToolActivity(tracker);
3451
+ tracker.markdown?.flush();
3452
+ process.stderr.write(receipt);
3453
+ activityHistory.add(receipt.trim());
3454
+ }
3455
+ }
3456
+ } };
3457
+ };
3519
3458
  const refreshSession = async () => {
3520
3459
  const refreshed = await sessionStore.get(session.sessionId);
3521
3460
  if (!refreshed)
@@ -3561,6 +3500,7 @@ Stopping the active operation; waiting for durable state and cleanup.
3561
3500
  session = selected;
3562
3501
  attachments.clear();
3563
3502
  readline.clearHistory();
3503
+ activityHistory.clear();
3564
3504
  messages = state?.messages ?? [];
3565
3505
  retainedTasks = taskSources(state?.metadata);
3566
3506
  };
@@ -3572,7 +3512,7 @@ Stopping the active operation; waiting for durable state and cleanup.
3572
3512
  `);
3573
3513
  return;
3574
3514
  }
3575
- const tracker = { streamedText: false };
3515
+ const { tracker, onEvent } = createTracker();
3576
3516
  session = await sessionStore.updateRun(session.sessionId, state.runId, { status: "running" });
3577
3517
  let result;
3578
3518
  try {
@@ -3582,8 +3522,8 @@ Stopping the active operation; waiting for durable state and cleanup.
3582
3522
  abortSignal,
3583
3523
  approvals: approvalResponses(state.pendingApprovals, approve, approve ? "Approved inline in chat." : "Denied inline in chat.")
3584
3524
  }, {
3585
- onEvent: streamSink({ json: false, jsonl: false }, tracker, !verbose),
3586
- resolveApprovals: terminalApprovalResolver(options.approvalMode ?? options.yes, (question) => readline.question(question), { select: (title, items) => readline.select(title, items), workspace: harness.config.workspace, sessionGrants })
3525
+ onEvent,
3526
+ resolveApprovals: consoleApprovals
3587
3527
  }));
3588
3528
  } catch {
3589
3529
  flushToolActivity(tracker);
@@ -3633,9 +3573,11 @@ Last failure: ${terminalRunFailure(failedState.error)} (${failedState.currentSte
3633
3573
  }, { color: terminalSupportsColor(Boolean(process.stderr.isTTY)), columns: process.stdout.columns ?? 80 }) + `
3634
3574
  `);
3635
3575
  process.stderr.write(`Ready · credential: ${credentials.store.source(harness.config.provider)} · account access is not checked until your first task.
3576
+ `);
3577
+ process.stderr.write(`While working: type a draft, Enter queues the next task, Up recalls the last queued task. Ctrl+C stops and clears the queue.
3636
3578
  `);
3637
3579
  process.stderr.write(options.approvalMode === "restricted" ? `Restricted mode: additional approvals are denied.
3638
- ` : options.yes ? `Automatic approvals are enabled; dependency access still requires permission.
3580
+ ` : options.yes ? `Automatic approvals are enabled within workspace and execution policies.
3639
3581
  ` : `Changes require your approval.
3640
3582
  `);
3641
3583
  const showSessionState = async () => {
@@ -3656,16 +3598,17 @@ ${summarizeApproval(approval)}
3656
3598
  await showSessionState();
3657
3599
  for (;; ) {
3658
3600
  try {
3659
- process.stdout.write(formatComposer({
3601
+ const pendingTurn = await hasActiveTurn();
3602
+ readline.setQueueEnabled(!pendingTurn);
3603
+ const submitted = await readline.compose({
3660
3604
  model: `${harness.config.provider}/${harness.config.model}`,
3605
+ reasoning: harness.config.reasoningEffort ?? "default",
3661
3606
  ...session.title ? { title: session.title } : {},
3662
3607
  status: session.runs.at(-1)?.status === "waiting_approval" ? "approval pending · /pending" : "ready",
3663
3608
  attachments: attachments.list().length,
3664
3609
  automaticApprovals: options.yes === true,
3665
3610
  ...options.approvalMode ? { approvalMode: options.approvalMode } : {}
3666
- }, process.stdout.columns));
3667
- const submitted = await readline.question(`
3668
- > `, true);
3611
+ });
3669
3612
  const literalInput = readline.lastSubmissionWasPaste || submitted.includes(`
3670
3613
  `);
3671
3614
  let prompt = literalInput ? submitted : submitted.trim();
@@ -3676,7 +3619,7 @@ ${summarizeApproval(approval)}
3676
3619
  if (["/menu", "/provider", "/providers", "/model", "/models"].includes(command)) {
3677
3620
  const selection = await navigateConsole(readline, {
3678
3621
  entry: command === "/menu" ? "menu" : command.startsWith("/provider") ? "provider" : "model",
3679
- current: { provider: harness.config.provider, model: harness.config.model },
3622
+ current: { provider: harness.config.provider, model: harness.config.model, reasoningEffort: harness.config.reasoningEffort ?? "default" },
3680
3623
  providers: providerAvailability(),
3681
3624
  sessions: async () => (await sessionStore.list({ limit: 200 })).map((item) => ({ value: item.sessionId, label: item.title ?? "Untitled conversation", detail: item.sessionId }))
3682
3625
  });
@@ -3693,9 +3636,9 @@ ${summarizeApproval(approval)}
3693
3636
  continue;
3694
3637
  }
3695
3638
  const portableMessages = compactHarnessMessages(messages);
3696
- await replaceHarness({ ...runtimeOptions, provider: parseProvider(selection.provider), model: selection.model }, routes);
3639
+ await replaceHarness({ ...runtimeOptions, provider: parseProvider(selection.provider), model: selection.model, reasoningEffort: selection.reasoningEffort ?? "default" }, routes);
3697
3640
  messages = portableMessages;
3698
- process.stderr.write(`Next turn: ${selection.provider}/${selection.model}; context was compacted.
3641
+ process.stderr.write(`Next turn: ${selection.provider}/${selection.model} · reasoning ${selection.reasoningEffort ?? "default"}; context was compacted.
3699
3642
  `);
3700
3643
  continue;
3701
3644
  }
@@ -3834,8 +3777,8 @@ ${summarizeApproval(approval)}
3834
3777
  continue;
3835
3778
  }
3836
3779
  if (command === "/sessions" || command.startsWith("/sessions ")) {
3837
- const summaries2 = await sessionStore.list({ search: command.slice(9).trim(), limit: 200 });
3838
- for (const summary of summaries2) {
3780
+ const summaries = await sessionStore.list({ search: command.slice(9).trim(), limit: 200 });
3781
+ for (const summary of summaries) {
3839
3782
  const selected = await sessionStore.get(summary.sessionId);
3840
3783
  if (!selected)
3841
3784
  continue;
@@ -3843,7 +3786,7 @@ ${summarizeApproval(approval)}
3843
3786
  process.stdout.write(sanitizeTerminalText(`${summary.sessionId} · ${summary.title ?? "(untitled)"} · ${summary.runCount} runs · ${durable?.status ?? "empty"}`) + `
3844
3787
  `);
3845
3788
  }
3846
- if (!summaries2.length)
3789
+ if (!summaries.length)
3847
3790
  process.stdout.write(`No sessions match in this workspace and scope.
3848
3791
  `);
3849
3792
  continue;
@@ -3884,12 +3827,30 @@ ${summarizeApproval(approval)}
3884
3827
  retainedTasks = [];
3885
3828
  attachments.clear();
3886
3829
  readline.clearHistory();
3830
+ activityHistory.clear();
3887
3831
  process.stderr.write(`Context cleared.
3888
3832
  `);
3889
3833
  continue;
3890
3834
  }
3891
3835
  if (command === "/status") {
3892
3836
  process.stderr.write(`${await statusLine()}
3837
+ `);
3838
+ continue;
3839
+ }
3840
+ if (command === "/activity" || command === "/activity clear") {
3841
+ if (command.endsWith(" clear"))
3842
+ activityHistory.clear();
3843
+ process.stdout.write(activityHistory.render());
3844
+ continue;
3845
+ }
3846
+ if (command === "/queue clear") {
3847
+ readline.clearQueue();
3848
+ process.stdout.write(`Queued tasks and draft cleared.
3849
+ `);
3850
+ continue;
3851
+ }
3852
+ if (command === "/queue") {
3853
+ process.stdout.write(readline.queueSummary() + `Use /queue clear to discard queued tasks.
3893
3854
  `);
3894
3855
  continue;
3895
3856
  }
@@ -3937,7 +3898,7 @@ ${summarizeApproval(approval)}
3937
3898
  const provider = parseProvider(value);
3938
3899
  const model = DEFAULT_PROVIDER_REGISTRY.descriptor(provider).defaultModel;
3939
3900
  const portableMessages = compactHarnessMessages(messages);
3940
- await replaceHarness({ ...runtimeOptions, provider, model }, routes);
3901
+ await replaceHarness({ ...runtimeOptions, provider, model, reasoningEffort: "default" }, routes);
3941
3902
  messages = portableMessages;
3942
3903
  process.stderr.write(`Next turn: ${provider}/${model}; context was compacted for a safe handoff.
3943
3904
  `);
@@ -3952,9 +3913,28 @@ ${summarizeApproval(approval)}
3952
3913
  continue;
3953
3914
  }
3954
3915
  const portableMessages = compactHarnessMessages(messages);
3955
- await replaceHarness({ ...runtimeOptions, model: value }, routes);
3916
+ const reasoningEffort = await chooseReasoning(readline, harness.config.provider, value);
3917
+ if (!reasoningEffort)
3918
+ continue;
3919
+ await replaceHarness({ ...runtimeOptions, model: value, reasoningEffort }, routes);
3956
3920
  messages = portableMessages;
3957
3921
  process.stderr.write(`Next turn: ${harness.config.provider}/${value}; context was compacted.
3922
+ `);
3923
+ continue;
3924
+ }
3925
+ if (command === "/reasoning" || command.startsWith("/reasoning ")) {
3926
+ const active2 = await hasActiveTurn();
3927
+ if (active2) {
3928
+ process.stderr.write(`Cannot change reasoning while run ${active2.runId} is ${active2.status}.
3929
+ `);
3930
+ continue;
3931
+ }
3932
+ const argument = command.slice("/reasoning".length).trim();
3933
+ const effort = argument ? reasoningEffortSchema.parse(argument) : await chooseReasoning(readline, harness.config.provider, harness.config.model, harness.config.reasoningEffort);
3934
+ if (!effort)
3935
+ continue;
3936
+ await replaceHarness({ ...runtimeOptions, reasoningEffort: effort }, routes);
3937
+ process.stderr.write(`Reasoning for next turns: ${effort}.
3958
3938
  `);
3959
3939
  continue;
3960
3940
  }
@@ -4112,7 +4092,7 @@ ${sanitizeTerminalText(prompt)}
4112
4092
  status: "created"
4113
4093
  });
4114
4094
  const turn = session.runs.at(-1);
4115
- const tracker = { streamedText: false };
4095
+ const { tracker, onEvent } = createTracker();
4116
4096
  const progress = consoleProgressGuard();
4117
4097
  let markedRunning = false;
4118
4098
  let result;
@@ -4160,9 +4140,9 @@ ${sanitizeTerminalText(prompt)}
4160
4140
  session = await sessionStore.updateRun(session.sessionId, runId, { status: "running" });
4161
4141
  markedRunning = true;
4162
4142
  }
4163
- await streamSink({ json: false, jsonl: false }, tracker, !verbose)(event);
4143
+ await onEvent(event);
4164
4144
  },
4165
- resolveApprovals: terminalApprovalResolver(options.approvalMode ?? options.yes, (question) => readline.question(question), { select: (title, items) => readline.select(title, items), workspace: harness.config.workspace, sessionGrants })
4145
+ resolveApprovals: consoleApprovals
4166
4146
  }));
4167
4147
  } catch (error) {
4168
4148
  flushToolActivity(tracker);
@@ -4590,6 +4570,175 @@ var manageChanges = async (options) => {
4590
4570
  process.exitCode = CLI_EXIT_CODES.runtimeError;
4591
4571
  };
4592
4572
 
4573
+ // src/persistence/state-backup-stream.ts
4574
+ var MAGIC = `ZHIVEX-STATE-SEGMENTS-1
4575
+ `;
4576
+ var STRING_CHARS = 8192;
4577
+ var MAX_FRAME_BYTES = 64 * 1024;
4578
+ function* canonicalChunks(value) {
4579
+ if (typeof value === "string") {
4580
+ yield '"';
4581
+ for (let i = 0;i < value.length; ) {
4582
+ let end = Math.min(i + STRING_CHARS, value.length);
4583
+ if (end < value.length && /[\uD800-\uDBFF]/.test(value[end - 1]))
4584
+ end--;
4585
+ yield JSON.stringify(value.slice(i, end)).slice(1, -1);
4586
+ i = end;
4587
+ }
4588
+ yield '"';
4589
+ } else if (value === null || typeof value !== "object") {
4590
+ yield JSON.stringify(value);
4591
+ } else if (Array.isArray(value)) {
4592
+ yield "[";
4593
+ for (let i = 0;i < value.length; i++) {
4594
+ if (i)
4595
+ yield ",";
4596
+ yield* canonicalChunks(value[i]);
4597
+ }
4598
+ yield "]";
4599
+ } else {
4600
+ yield "{";
4601
+ let first = true;
4602
+ for (const key of Object.keys(value).sort()) {
4603
+ if (!first)
4604
+ yield ",";
4605
+ first = false;
4606
+ yield* canonicalChunks(key);
4607
+ yield ":";
4608
+ yield* canonicalChunks(value[key]);
4609
+ }
4610
+ yield "}";
4611
+ }
4612
+ }
4613
+ function* tokens(value) {
4614
+ if (typeof value === "string") {
4615
+ for (let i = 0;i < value.length; i += STRING_CHARS)
4616
+ yield ["text", value.slice(i, i + STRING_CHARS)];
4617
+ yield ["string"];
4618
+ } else if (value === null || typeof value !== "object")
4619
+ yield ["value", value];
4620
+ else {
4621
+ yield [Array.isArray(value) ? "array" : "object"];
4622
+ if (Array.isArray(value))
4623
+ for (const item of value)
4624
+ yield* tokens(item);
4625
+ else
4626
+ for (const [key, item] of Object.entries(value)) {
4627
+ yield* tokens(key);
4628
+ yield* tokens(item);
4629
+ }
4630
+ yield ["end"];
4631
+ }
4632
+ }
4633
+ function* segmentedChunks(value) {
4634
+ yield MAGIC;
4635
+ for (const token of tokens(value))
4636
+ yield `${JSON.stringify(token)}
4637
+ `;
4638
+ }
4639
+ async function readBackupTransport(source, legacyMaxBytes) {
4640
+ const fail = () => {
4641
+ throw new Error("Harness state backup segment stream is invalid or incomplete.");
4642
+ };
4643
+ const stack = [];
4644
+ let root;
4645
+ let hasRoot = false;
4646
+ let pieces = [];
4647
+ let pending = Buffer.alloc(0);
4648
+ let segmented;
4649
+ const legacy = [];
4650
+ let legacyBytes = 0;
4651
+ const append = (value) => {
4652
+ const parent = stack.at(-1);
4653
+ if (!parent) {
4654
+ if (hasRoot)
4655
+ fail();
4656
+ root = value;
4657
+ hasRoot = true;
4658
+ } else if (Array.isArray(parent.value))
4659
+ parent.value.push(value);
4660
+ else if (parent.key === undefined) {
4661
+ if (typeof value !== "string" || Object.hasOwn(parent.value, value))
4662
+ fail();
4663
+ parent.key = value;
4664
+ } else {
4665
+ Object.defineProperty(parent.value, parent.key, { value, enumerable: true, writable: true, configurable: true });
4666
+ delete parent.key;
4667
+ }
4668
+ };
4669
+ const consume = (line) => {
4670
+ if (line.length > MAX_FRAME_BYTES)
4671
+ fail();
4672
+ const token = JSON.parse(line.toString("utf8"));
4673
+ if (!Array.isArray(token))
4674
+ fail();
4675
+ const frame = token;
4676
+ const [kind, value] = frame;
4677
+ if (kind === "text" && frame.length === 2 && typeof value === "string") {
4678
+ pieces.push(value);
4679
+ return;
4680
+ }
4681
+ if (kind === "string" && frame.length === 1) {
4682
+ append(pieces.join(""));
4683
+ pieces = [];
4684
+ return;
4685
+ }
4686
+ if (pieces.length)
4687
+ fail();
4688
+ if (kind === "value" && frame.length === 2 && (value === null || typeof value === "boolean" || typeof value === "number"))
4689
+ append(value);
4690
+ else if ((kind === "array" || kind === "object") && frame.length === 1) {
4691
+ if (stack.length >= 512)
4692
+ fail();
4693
+ const value2 = kind === "array" ? [] : {};
4694
+ append(value2);
4695
+ stack.push({ value: value2 });
4696
+ } else if (kind === "end" && frame.length === 1) {
4697
+ const parent = stack.pop();
4698
+ if (!parent || parent.key !== undefined)
4699
+ fail();
4700
+ } else
4701
+ fail();
4702
+ };
4703
+ for await (const chunk of readRegularFileChunksNoFollow(source, {
4704
+ label: "Harness state backup",
4705
+ requireSingleLink: true,
4706
+ requirePrivate: true
4707
+ })) {
4708
+ pending = Buffer.concat([pending, chunk]);
4709
+ if (segmented === undefined) {
4710
+ if (pending.length < Buffer.byteLength(MAGIC))
4711
+ continue;
4712
+ segmented = pending.subarray(0, MAGIC.length).toString() === MAGIC;
4713
+ if (segmented)
4714
+ pending = pending.subarray(MAGIC.length);
4715
+ }
4716
+ if (!segmented) {
4717
+ legacyBytes += pending.length;
4718
+ if (legacyBytes > legacyMaxBytes)
4719
+ throw new Error("Harness state backup exceeds the legacy size limit.");
4720
+ legacy.push(pending);
4721
+ pending = Buffer.alloc(0);
4722
+ continue;
4723
+ }
4724
+ let newline;
4725
+ while ((newline = pending.indexOf(10)) !== -1) {
4726
+ consume(pending.subarray(0, newline));
4727
+ pending = pending.subarray(newline + 1);
4728
+ }
4729
+ if (pending.length > MAX_FRAME_BYTES)
4730
+ fail();
4731
+ }
4732
+ if (!segmented) {
4733
+ if (legacyBytes + pending.length > legacyMaxBytes)
4734
+ throw new Error("Harness state backup exceeds the legacy size limit.");
4735
+ return JSON.parse(Buffer.concat([...legacy, pending]).toString("utf8"));
4736
+ }
4737
+ if (pending.length || stack.length || pieces.length || !hasRoot)
4738
+ fail();
4739
+ return root;
4740
+ }
4741
+
4593
4742
  // src/persistence/workspace-checkpoints.ts
4594
4743
  import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
4595
4744
  import { z as z3 } from "zod";
@@ -4621,6 +4770,7 @@ import { chmod, link, lstat as lstat3, open as open2, realpath, unlink as unlink
4621
4770
  import path5 from "node:path";
4622
4771
  import { z as z4 } from "zod";
4623
4772
  import { normalizeAgentRunState } from "@zhivex-ai/agents";
4773
+ import { createSqliteAgentRunStore } from "@zhivex-ai/agents/ops";
4624
4774
  var HARNESS_STATE_BACKUP_SCHEMA_VERSION = 1;
4625
4775
  var HARNESS_STATE_BACKUP_MAX_BYTES = 64 * 1024 * 1024;
4626
4776
  var terminalRunStatuses = new Set(["completed", "failed", "cancelled", "timed_out"]);
@@ -4755,7 +4905,12 @@ var canonicalJson = (value) => {
4755
4905
  return `[${value.map(canonicalJson).join(",")}]`;
4756
4906
  return `{${Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(",")}}`;
4757
4907
  };
4758
- var checksumPayload = (payload) => `sha256:${createHash3("sha256").update(canonicalJson(payload)).digest("hex")}`;
4908
+ var checksumPayload = (payload) => {
4909
+ const hash = createHash3("sha256");
4910
+ for (const chunk of canonicalChunks(payload))
4911
+ hash.update(chunk);
4912
+ return `sha256:${hash.digest("hex")}`;
4913
+ };
4759
4914
  var stableKey = (kind, value) => createHash3("sha256").update(kind).update("\x00").update(value).digest("hex");
4760
4915
  var scopeValue = (config) => `${config.scope.tenantId}\x00${config.scope.userId ?? ""}\x00${config.scope.namespace ?? ""}`;
4761
4916
  var scopePrefix = (config) => `${encodeURIComponent(config.scope.namespace ?? "default")}:${encodeURIComponent(config.scope.tenantId)}:${encodeURIComponent(config.scope.userId ?? "*")}:`;
@@ -4787,8 +4942,8 @@ var validateBudgetLedger = (config, run, identities) => {
4787
4942
  if (!/^sha256:[a-f0-9]{64}$/.test(id) || !value || typeof value !== "object" || Array.isArray(value) || value.status !== "confirmed") {
4788
4943
  throw new HarnessStateConflictError("Budget ledger retains unresolved reservations or unknown consumption.");
4789
4944
  }
4790
- const tokens = value.tokens;
4791
- if (!tokens || typeof tokens !== "object" || Array.isArray(tokens) || ![tokens.inputTokens, tokens.outputTokens, tokens.totalTokens].every((amount) => typeof amount === "number" && Number.isSafeInteger(amount) && amount >= 0) || tokens.totalTokens < tokens.inputTokens + tokens.outputTokens) {
4945
+ const tokens2 = value.tokens;
4946
+ if (!tokens2 || typeof tokens2 !== "object" || Array.isArray(tokens2) || ![tokens2.inputTokens, tokens2.outputTokens, tokens2.totalTokens].every((amount) => typeof amount === "number" && Number.isSafeInteger(amount) && amount >= 0) || tokens2.totalTokens < tokens2.inputTokens + tokens2.outputTokens) {
4792
4947
  throw new HarnessStateConflictError("Invalid budget ledger token receipt.");
4793
4948
  }
4794
4949
  }
@@ -4890,8 +5045,13 @@ var readPayload = async (config, recordedWorkspace, accessLease) => {
4890
5045
  if (recordedWorkspace && database.query("SELECT version FROM zhivex_cli_session_schema WHERE singleton=1").get()?.version !== HARNESS_SESSION_SCHEMA_VERSION)
4891
5046
  throw new HarnessStateConflictError("Archived session schema is incompatible.");
4892
5047
  const allRunRows = database.query("SELECT run_id, state_json, updated_at_ms FROM zhivex_agent_runs ORDER BY run_id").all();
5048
+ let historyReader;
4893
5049
  const runRows = allRunRows.flatMap((row) => {
4894
- const state = validatedRunState(parseJsonRecord(row.state_json, `Run ${row.run_id}`), `Run ${row.run_id}`);
5050
+ const record = parseJsonRecord(row.state_json, `Run ${row.run_id}`);
5051
+ const hydrated = record.checkpointHistory ? (historyReader ??= createSqliteAgentRunStore({ db: sqliteAdapter(database), history: "incremental" })).load(String(record.runId), record.scope) : record;
5052
+ if (hydrated instanceof Promise || !hydrated)
5053
+ throw new HarnessStateConflictError("Cannot hydrate state backup history.");
5054
+ const state = validatedRunState(hydrated, `Run ${row.run_id}`);
4895
5055
  return stateMatchesScope(config, state) ? [{ row, state }] : [];
4896
5056
  });
4897
5057
  const runs = runRows.map(({ row, state }) => {
@@ -5051,7 +5211,19 @@ var writePrivateBackup = async (target, contents) => {
5051
5211
  try {
5052
5212
  handle = await open2(staged, fsConstants3.O_WRONLY | fsConstants3.O_CREAT | fsConstants3.O_EXCL | fsConstants3.O_NOFOLLOW, 384);
5053
5213
  stagedExists = true;
5054
- await handle.writeFile(contents, "utf8");
5214
+ let pending = [];
5215
+ let pendingBytes = 0;
5216
+ for (const chunk of contents) {
5217
+ pending.push(chunk);
5218
+ pendingBytes += Buffer.byteLength(chunk);
5219
+ if (pendingBytes >= 64 * 1024) {
5220
+ await handle.writeFile(pending.join(""), "utf8");
5221
+ pending = [];
5222
+ pendingBytes = 0;
5223
+ }
5224
+ }
5225
+ if (pending.length)
5226
+ await handle.writeFile(pending.join(""), "utf8");
5055
5227
  await handle.sync();
5056
5228
  await chmod(staged, 384);
5057
5229
  await handle.close();
@@ -5078,12 +5250,13 @@ var writePrivateBackup = async (target, contents) => {
5078
5250
  };
5079
5251
  var exportHarnessStateBackup = async (config, target) => {
5080
5252
  const bundle = await createHarnessStateBackup(config);
5081
- const serialized = `${JSON.stringify(bundle, null, 2)}
5082
- `;
5083
- if (Buffer.byteLength(serialized) > HARNESS_STATE_BACKUP_MAX_BYTES) {
5084
- throw new HarnessStateConflictError("State backup exceeds the supported size limit.");
5253
+ let bytes = 0;
5254
+ for (const chunk of canonicalChunks(bundle)) {
5255
+ bytes += Buffer.byteLength(chunk);
5256
+ if (bytes > HARNESS_STATE_BACKUP_MAX_BYTES)
5257
+ break;
5085
5258
  }
5086
- await writePrivateBackup(target, serialized);
5259
+ await writePrivateBackup(target, bytes > HARNESS_STATE_BACKUP_MAX_BYTES ? segmentedChunks(bundle) : canonicalChunks(bundle));
5087
5260
  return {
5088
5261
  schemaVersion: HARNESS_STATE_BACKUP_SCHEMA_VERSION,
5089
5262
  kind: "state-export",
@@ -5093,19 +5266,14 @@ var exportHarnessStateBackup = async (config, target) => {
5093
5266
  };
5094
5267
  };
5095
5268
  var readHarnessStateBackup = async (source) => {
5096
- const { contents, stat: stat2 } = await readRegularFileNoFollow(path5.resolve(source), {
5097
- label: "Harness state backup",
5098
- maxBytes: HARNESS_STATE_BACKUP_MAX_BYTES,
5099
- requireSingleLink: true
5100
- });
5101
- if ((stat2.mode & 63) !== 0) {
5102
- throw new HarnessWorkspaceError("Harness state backup permissions must not grant group or other access.");
5103
- }
5104
5269
  let input;
5105
5270
  try {
5106
- input = JSON.parse(contents.toString("utf8"));
5271
+ input = await readBackupTransport(path5.resolve(source), HARNESS_STATE_BACKUP_MAX_BYTES);
5107
5272
  } catch (error) {
5108
- throw new HarnessStateConflictError("Harness state backup is not valid JSON.", { cause: error });
5273
+ if (error instanceof SyntaxError) {
5274
+ throw new HarnessStateConflictError("Harness state backup is not valid JSON.", { cause: error });
5275
+ }
5276
+ throw error;
5109
5277
  }
5110
5278
  return validateStateBackupBundle(input);
5111
5279
  };
@@ -5506,7 +5674,7 @@ var main = async (argv = process.argv.slice(2)) => {
5506
5674
  parsedOptions = { ...parsedOptions, prompt: await readStdinTask() };
5507
5675
  }
5508
5676
  if (parsedOptions.serviceFile) {
5509
- const { runServiceCli } = await import("./service-client-cli-s3ee2hrj.js");
5677
+ const { runServiceCli } = await import("./service-client-cli-yx56n0hc.js");
5510
5678
  await runServiceCli(parsedOptions, annotateCliStreamError);
5511
5679
  return;
5512
5680
  }