jg-agent-hq 0.5.1 → 1.0.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 (103) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +5 -1
  3. package/api/README.md +8 -4
  4. package/dist/actions/actions.d.ts +17 -17
  5. package/dist/actions/actions.js +56 -37
  6. package/dist/actions/reads.d.ts +4 -4
  7. package/dist/actions/reads.js +16 -9
  8. package/dist/cli/cli.js +80 -21
  9. package/dist/contracts/constants.d.ts +9 -0
  10. package/dist/contracts/constants.js +10 -0
  11. package/dist/contracts/index.d.ts +1 -1
  12. package/dist/contracts/index.js +1 -1
  13. package/dist/errors/utilities.js +2 -2
  14. package/dist/handoffs/handoffs.d.ts +25 -25
  15. package/dist/handoffs/handoffs.js +93 -77
  16. package/dist/ipc/client.d.ts +13 -13
  17. package/dist/ipc/client.js +53 -25
  18. package/dist/ipc/framing.d.ts +1 -1
  19. package/dist/ipc/framing.js +15 -5
  20. package/dist/ipc/server.d.ts +1 -1
  21. package/dist/ipc/server.js +26 -8
  22. package/dist/locks/locks.d.ts +38 -38
  23. package/dist/locks/locks.js +99 -69
  24. package/dist/plans/admission.d.ts +36 -36
  25. package/dist/plans/admission.js +42 -39
  26. package/dist/plans/authority.d.ts +1 -1
  27. package/dist/plans/authority.js +7 -4
  28. package/dist/plans/evidence.d.ts +4 -4
  29. package/dist/plans/evidence.js +64 -29
  30. package/dist/plans/files.d.ts +8 -8
  31. package/dist/plans/files.js +59 -21
  32. package/dist/plans/finalization.d.ts +2 -2
  33. package/dist/plans/finalization.js +8 -5
  34. package/dist/plans/plans.d.ts +76 -76
  35. package/dist/plans/plans.js +91 -85
  36. package/dist/plans/projections.d.ts +2 -2
  37. package/dist/plans/projections.js +18 -7
  38. package/dist/plans/reconciliation.d.ts +3 -3
  39. package/dist/plans/reconciliation.js +13 -7
  40. package/dist/plans/revisions.d.ts +7 -7
  41. package/dist/plans/revisions.js +13 -10
  42. package/dist/plans/utilities.d.ts +2 -2
  43. package/dist/plans/utilities.js +36 -11
  44. package/dist/providers/claude/claude.d.ts +6 -6
  45. package/dist/providers/claude/claude.js +29 -16
  46. package/dist/providers/claude/control/control.d.ts +19 -19
  47. package/dist/providers/claude/control/control.js +45 -33
  48. package/dist/providers/claude/control/permissions.js +5 -2
  49. package/dist/providers/claude/control/skills.d.ts +1 -1
  50. package/dist/providers/claude/control/skills.js +12 -5
  51. package/dist/providers/claude/control/stream.d.ts +1 -1
  52. package/dist/providers/claude/control/stream.js +6 -3
  53. package/dist/providers/claude/history/history.model.d.ts +2 -2
  54. package/dist/providers/claude/history/history.model.js +41 -22
  55. package/dist/providers/claude/history/utilities.d.ts +8 -3
  56. package/dist/providers/claude/history/utilities.js +68 -17
  57. package/dist/providers/claude/rpc.d.ts +3 -3
  58. package/dist/providers/claude/rpc.js +56 -19
  59. package/dist/providers/claude/transformers.d.ts +1 -1
  60. package/dist/providers/claude/transformers.js +7 -4
  61. package/dist/providers/claude/types.d.ts +7 -1
  62. package/dist/providers/claude/types.js +6 -1
  63. package/dist/providers/codex/approvals/utilities.d.ts +1 -1
  64. package/dist/providers/codex/approvals/utilities.js +6 -3
  65. package/dist/providers/codex/codex.d.ts +2 -2
  66. package/dist/providers/codex/codex.js +48 -21
  67. package/dist/providers/codex/rpc.d.ts +2 -2
  68. package/dist/providers/codex/rpc.js +60 -19
  69. package/dist/providers/codex/transformers.d.ts +4 -4
  70. package/dist/providers/codex/transformers.js +4 -4
  71. package/dist/providers/codex/types.d.ts +12 -12
  72. package/dist/providers/codex/validations.d.ts +1 -1
  73. package/dist/providers/codex/validations.js +7 -3
  74. package/dist/providers/process.d.ts +1 -1
  75. package/dist/providers/process.js +16 -7
  76. package/dist/providers/types.d.ts +3 -3
  77. package/dist/runtime/browser/browser.js +14 -5
  78. package/dist/runtime/runtime.d.ts +13 -13
  79. package/dist/runtime/runtime.js +49 -24
  80. package/dist/runtime/state/state.d.ts +18 -18
  81. package/dist/runtime/state/state.js +100 -45
  82. package/dist/runtime/types.d.ts +37 -37
  83. package/dist/sessions/sessions.d.ts +4 -4
  84. package/dist/sessions/sessions.js +15 -9
  85. package/dist/sessions/utilities.d.ts +1 -1
  86. package/dist/sessions/utilities.js +16 -6
  87. package/dist/skills/skills.d.ts +10 -10
  88. package/dist/skills/skills.js +27 -17
  89. package/dist/store/browser/browser.model.d.ts +1 -1
  90. package/dist/store/browser/browser.model.js +7 -4
  91. package/dist/store/control/control.model.d.ts +4 -4
  92. package/dist/store/control/control.model.js +10 -7
  93. package/dist/store/handoffs/handoffs.model.d.ts +3 -3
  94. package/dist/store/handoffs/handoffs.model.js +9 -6
  95. package/dist/store/plans/plans.model.d.ts +3 -3
  96. package/dist/store/plans/plans.model.js +29 -14
  97. package/dist/store/store.d.ts +1 -1
  98. package/dist/store/store.js +42 -13
  99. package/dist/store/store.worker.js +8 -4
  100. package/dist/store/types.d.ts +52 -51
  101. package/dist/store/types.js +3 -1
  102. package/errors/README.md +62 -62
  103. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0
4
+
5
+ - Use `jg-exception` with named string codes for runtime, lock, plan, provider, client and tooling failures. Preserve codes through SQLite-worker responses and publish browser-safe client codes alongside their safe messages. This changes the Node error contract; callers should handle the documented named codes.
6
+ - Use the shared Astro UI 0.4.0, `jg-site` metadata helpers and `jg-playwright` browser checks. Remove `error-message-utils` from the runtime and website.
7
+ - Redesign the public website around Claude Code and Codex collaboration, a copyable API example, the available runtime capabilities and section navigation for the documentation.
8
+
9
+ ## 0.5.2
10
+
11
+ - Resume inactive managed Codex sessions retained as loaded by native unsubscribe, preserving explicit handoff and active/uncertain ownership guards.
12
+ - Qualify Claude transcript formats 2.1.296 and 2.1.299, preserve in-workspace working-directory changes, and refresh reader projections when their interpretation changes. Keep executable qualification separate and reject workspace/symlink escapes.
13
+ - Await native-fixture cleanup after test timeouts and give the full 1,024-milestone revision workflow a focused CI budget. Add a real child-runner timeout/isolation regression.
14
+
3
15
  ## 0.5.1
4
16
 
5
17
  - Qualify Claude Code 2.1.306 history alongside 2.1.304, including transcripts spanning both versions. Continue to reject unqualified CLI and transcript versions.
package/README.md CHANGED
@@ -31,6 +31,8 @@ Short metadata operations use `withHqLock`. Long-lived runtime ownership must us
31
31
 
32
32
  See the [API reference](api/README.md) and [error contracts](errors/README.md).
33
33
 
34
+ Intentional failures use `Exception` from `jg-exception`, with the named string codes in the error reference. Local clients use `PUBLIC_ERROR_CODES` and `PUBLIC_ERROR_MESSAGES` from `jg-agent-hq/contracts` for errors translated at the IPC boundary. Safe classifications remain separate from private diagnostic causes.
35
+
34
36
  ## Local controller
35
37
 
36
38
  Install Codex and sign in through its native CLI, then start `jg-agent-hq serve --state-root .runtime --workspace-root /path/to/workspace`. Run `jg-agent-hq status --state-root .runtime` in a client. `threads`, `thread`, `turns`, `items` and `usage` read native information; `stop` waits for confirmed runtime shutdown.
@@ -41,7 +43,7 @@ The controller owns its SQLite worker and native connections independently of cl
41
43
 
42
44
  The current read contracts were exercised against Codex 0.163.0 and Claude Code versions 2.1.304 and 2.1.306 on Linux. Unsupported native responses fail explicitly. `jg-agent-hq/contracts` contains browser-safe schemas and types; importing it does not import the host runtime.
43
45
 
44
- Use `--provider claude` for Claude history and usage; the default is `codex`. Claude connects lazily through its native structured CLI, with normal settings enabled and no user prompt. Usage requests explicitly disable the optional transcript-behavior scan. Its local history reader accepts only the qualified 2.1.304 and 2.1.306 versions, including transcripts containing both, keeps byte locations rather than conversation bodies in SQLite, and reports unsupported history as unavailable. File history cannot establish current execution state. See the [reader limits and freshness contract](api/README.md#claude-history).
46
+ Use `--provider claude` for Claude history and usage; the default is `codex`. Claude connects lazily through its native structured CLI, with normal settings enabled and no user prompt. Usage requests explicitly disable the optional transcript-behavior scan. Its local history reader accepts only qualified transcript versions 2.1.296, 2.1.299, 2.1.304 and 2.1.306, including transcripts spanning these versions, keeps byte locations rather than conversation bodies in SQLite, and reports unsupported history as unavailable. File history cannot establish current execution state. See the [reader limits and freshness contract](api/README.md#claude-history).
45
47
 
46
48
  Explicit actions create, name, adopt, resume and close threads; send idle messages; steer an exact running turn; queue the next message; interrupt; and answer native questions or permissions. Actions have stable UUIDs and durable receipts. Repeating the same request returns its receipt; changing that UUID's payload conflicts. A lost acknowledgement stays uncertain until the exact native response arrives or the operator makes an explicit recovery choice. HQ never resends uncertain input.
47
49
 
@@ -105,4 +107,6 @@ Tests consume built public exports. Unit tests cover application-owned decisions
105
107
 
106
108
  The website's README and `docs/website.md` define its build-once check composition, category-specific scripts and owned preview/browser lifecycle. Root and website checks must both pass for task completion when workspace configuration or canonical site content changes.
107
109
 
110
+ The runtime fixture teardown is registered with the test runner as well as the callback, so a test timeout cannot restore environment variables during the next test.
111
+
108
112
  Current local checks establish the tested host only. Release qualification must include the Node engines floor and current LTS; macOS operation requires its own runtime evidence.
package/api/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Import the named exports from `jg-agent-hq`. Internal module paths are private.
4
4
 
5
+ Intentional Node API failures use `Exception` from `jg-exception`. The [error reference](../errors/README.md) defines their named string codes and stable messages. `PUBLIC_ERROR_CODES` and `PUBLIC_ERROR_MESSAGES` are available from `jg-agent-hq/contracts` for the seven safe client classifications. SQLite-worker responses retain the originating application code; arbitrary worker failures become `JG_AGENT_HQ_STORE_FAILED`. Browser and IPC error bodies expose only their established safe classification and message.
6
+
5
7
  ## Lock lifecycle
6
8
 
7
9
  - `acquireHqLock(root, command, options?)` acquires `.runtime/hq.lock` and returns an asynchronous release function. The caller owns the trusted state root and supplies a non-empty, single-line operation label. Surrounding whitespace is trimmed. The returned release function is idempotent and refuses a replaced owner.
@@ -48,11 +50,11 @@ Threads expose native state, `historyState` (`available` or `unavailable`), owne
48
50
 
49
51
  Claude connects on its first read through `claude --print --input-format stream-json --output-format stream-json --verbose`. No user prompt, bare mode, permission bypass or SDK dependency is used. Runtime status reports this adapter as `idle`, `ready` or `unavailable`, with its observed CLI version. An unavailable native lane requires restarting the host after resolving the native problem.
50
52
 
51
- The qualified CLI interface has no session-history listing operation. The Claude-owned reader therefore uses direct UUID JSONL files in the native project's directory beneath `CLAUDE_CONFIG_DIR`, or the normal `.claude` directory. The CLI and each versioned transcript record must independently match a qualified version: 2.1.304 or 2.1.306. A transcript may contain both qualified versions. Other formats, encoded project keys longer than 200 characters, directory/file links, malformed records and records larger than 1 MiB report unavailable. Discovery never recurses into subdirectories, including excluded directories. Unsupported discovered sessions have `historyState: unavailable`; they do not prevent supported sessions from being listed.
53
+ The qualified CLI interface has no session-history listing operation. The Claude-owned reader therefore uses direct UUID JSONL files in the native project's directory beneath `CLAUDE_CONFIG_DIR`, or the normal `.claude` directory. The running CLI must match 2.1.304 or 2.1.306. Transcript records may independently use the qualified formats 2.1.296, 2.1.299, 2.1.304 and 2.1.306, including multiple qualified formats within one session. Qualifying a stored record format does not qualify that executable version. Other formats, encoded project keys longer than 200 characters, directory/file links, malformed records and records larger than 1 MiB report unavailable. Discovery never recurses into subdirectories, including excluded directories. Unsupported discovered sessions have `historyState: unavailable`; they do not prevent supported sessions from being listed.
52
54
 
53
- Discovery verifies the workspace recorded inside each transcript before returning its identity or title. A matching native directory name alone is insufficient because punctuation replacement can produce colliding directory keys. Foreign workspaces are omitted. If a file's workspace cannot be established, discovery reports unavailable rather than inventing an empty result.
55
+ Discovery verifies the workspace recorded inside each transcript before returning its identity or title. A matching native directory name alone is insufficient because punctuation replacement can produce colliding directory keys. Native working directories may be the selected workspace or its subdirectories, including removed subdirectories. The nearest existing path ancestor must resolve inside the workspace without excluded segments; sibling paths and symlink escapes are rejected. Foreign workspaces are omitted. If a file's workspace cannot be established, discovery reports unavailable rather than inventing an empty result.
54
56
 
55
- The existing SQLite worker incrementally records file identity, complete-line offsets, message identities and parent links. It does not save message bodies. Each worker batch inspects at most 128 directory entries and reads at most 4 MiB of new transcript bytes, one file at a time. The adapter continues batches with a host-event-loop yield between them, within a 30-second operation budget. If that budget is reached, the read reports unavailable and subsequent reads continue indexing. At most four incomplete discovery iterators remain open. Shutdown closes them. Discovery continuations use that projection without starting another directory scan; start a fresh first page to refresh discovery. Removed files are discarded from the projection after a complete refresh. No additional cache infrastructure or browser cache is used.
57
+ The existing SQLite worker incrementally records file identity, complete-line offsets, message identities and parent links. It does not save message bodies. File identity includes the reader projection revision and qualified record formats, so a reader capability change refreshes rejected or obsolete projections from native files. Each worker batch inspects at most 128 directory entries and reads at most 4 MiB of new transcript bytes, one file at a time. The adapter continues batches with a host-event-loop yield between them, within a 30-second operation budget. If that budget is reached, the read reports unavailable and subsequent reads continue indexing. At most four incomplete discovery iterators remain open. Shutdown closes them. Discovery continuations use that projection without starting another directory scan; start a fresh first page to refresh discovery. Removed files are discarded from the projection after a complete refresh. No additional cache infrastructure or browser cache is used.
56
58
 
57
59
  Current-chain membership and turn starts are indexed as metadata. Ordinary item and turn continuations seek those indexes; they do not walk the full chain per page. A changed branch rebuilds membership before exposing that revision. Repeated native message identities can replace content while retaining their parent; a changed parent for the same identity is an unsupported format.
58
60
 
@@ -84,6 +86,8 @@ Message and queue input is a closed union: `{ kind: 'text', text }` or `{ kind:
84
86
 
85
87
  Names use lowercase ASCII letters, digits and hyphens, start with a letter or digit, and have at most 32 characters. Names are unique without regard to case in one state directory. Creation and adoption resolve collisions atomically with ordinal suffixes such as `build-usage-2`, truncating the base to keep the complete name within 32 characters. Explicit rename rejects an occupied or pending name. Provider/native-session pairs remain unique. At most 32 native sessions are controlled concurrently; close idle sessions to release capacity. Native discovery remains independent of that limit. Confirm that other native CLI controllers are closed before adoption, resume or recovery. An unloaded native status cannot prove another CLI has stopped. HQ provides exclusive coordination within one canonical state directory; it does not impose a global lock on independent CLIs. Recovery is permitted only after the producing runtime has stopped and its ownership evidence has been inspected.
86
88
 
89
+ Codex may keep an idle native thread loaded after `thread/unsubscribe`. An explicitly confirmed resume may reacquire that thread only when its known HQ control is inactive and managed. Active native threads, uncertain HQ ownership and already controlled idle sessions remain blocked. Other adoption and recovery checks are unchanged.
90
+
87
91
  Receipts separate `delivery` (`queued`, `submitted`, `acknowledged`, `uncertain`, `failed`, `canceled`) from nullable native turn `completion` (`completed`, `failed`, `interrupted`). Acknowledgement means the exact native request was accepted, not that the turn or tool completed. Permission replies remain submitted after a pipe write and become acknowledged only when Codex resolves the exact native request or Claude returns a tool-result block for the exact tool-use identity. Claude cancellation expires the prompt and leaves an unacknowledged written reply uncertain. Resolution does not prove a tool ran. The receipt includes HQ/native identity, request phase, digest, timestamps, safe failure classification and optional related action. Only selected receipts expose undelivered `queuedText` and `queuedSkill`; collection reads omit both. Queued native skill selections contain a native name and nullable source path, never skill content. Both are cleared after delivery acknowledgement or cancellation.
88
92
 
89
93
  A missing acknowledgement never triggers an automatic resend. Exact late responses reconcile delivery, including completion that arrived first. Native output loss marks the affected provider controls and submitted work uncertain. A Claude process failure affects its own session; a Codex transport failure affects its Codex cohort. Restart quarantines controls from an interrupted runtime; explicit recovery preserves the HQ identity and name. Successful shutdown proves native process-group exit before marking controls inactive. Unknown creation without a native identity remains uncertain.
@@ -100,7 +104,7 @@ Prompts expose `replyActionId` as soon as a reply is admitted, so reconnecting c
100
104
 
101
105
  ### Claude native control
102
106
 
103
- Each controlled session has one persistent native `claude` process in the authorized workspace. HQ adds structured input/output, replayed user-message acknowledgements, partial-message output and `--permission-prompt-tool=stdio`. It does not set a model, replace instructions/settings, disable session persistence, bypass permissions or enable a bare mode. Native skills remain available. Control was exercised against CLI 2.1.304. Native idle initialization, session identity and matching acknowledgements establish capability; a changed version label alone does not disable compatible controls. File history retains its separate qualified-version check.
107
+ Each controlled session has one persistent native `claude` process in the authorized workspace. HQ adds structured input/output, replayed user-message acknowledgements, partial-message output and `--permission-prompt-tool=stdio`. It does not set a model, replace instructions/settings, disable session persistence, bypass permissions or enable a bare mode. Native skills remain available. Control was exercised against CLI 2.1.304 and 2.1.306. Native idle initialization, session identity and matching acknowledgements establish capability; a changed version label alone does not disable compatible controls. File history retains its separate qualified-version check.
104
108
 
105
109
  Creation chooses a native UUID without inference, then names that exact session. Resume uses native `--resume` and verifies the session-id fence on the name control before marking the session idle. The operator's handoff confirmation establishes that another CLI controller is closed; file timestamps and file-backed unknown states do not establish this. Independently running CLIs are outside HQ's state-directory coordination boundary.
106
110
 
@@ -14,14 +14,14 @@ export declare const createActions: (native: ICodexProvider, claude: ReturnType<
14
14
  * Returns after durable admission; the native operation outlives this client request.
15
15
  * @returns The durable admission receipt after validation.
16
16
  * @throws
17
- * - -1: The HQ action conflicts with its recorded state
18
- * - -1: The HQ skill is unavailable
19
- * - -1: The HQ request is invalid
20
- * - -1: The HQ target is outside the allowed workspaces
21
- * - -1: The Codex integration failed
22
- * - -1: The Claude integration failed
23
- * - -1: The Claude history is unavailable
24
- * - -1: The HQ store failed
17
+ * - JG_AGENT_HQ_ACTION_CONFLICT: The HQ action conflicts with its recorded state
18
+ * - JG_AGENT_HQ_SKILL_UNAVAILABLE: The HQ skill is unavailable
19
+ * - JG_AGENT_HQ_REQUEST_INVALID: The HQ request is invalid
20
+ * - JG_AGENT_HQ_TARGET_FORBIDDEN: The HQ target is outside the allowed workspaces
21
+ * - JG_AGENT_HQ_CODEX_FAILED: The Codex integration failed
22
+ * - JG_AGENT_HQ_CLAUDE_FAILED: The Claude integration failed
23
+ * - JG_AGENT_HQ_CLAUDE_HISTORY_UNAVAILABLE: The Claude history is unavailable
24
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
25
25
  */
26
26
  submit(action: IActionEnvelope): Promise<IActionReceipt>;
27
27
  /** Reads only current in-memory prompts for the selected managed thread. */
@@ -30,21 +30,21 @@ export declare const createActions: (native: ICodexProvider, claude: ReturnType<
30
30
  * Quiesces the selected managed cohort without stopping unrelated controllers.
31
31
  * @returns A promise resolved only after interruption, background cleanup and release evidence.
32
32
  * @throws
33
- * - -1: The HQ action conflicts with its recorded state
34
- * - -1: The HQ target is outside the allowed workspaces
35
- * - -1: The HQ store failed
36
- * - -1: The Codex integration failed
37
- * - -1: The Claude integration failed
33
+ * - JG_AGENT_HQ_ACTION_CONFLICT: The HQ action conflicts with its recorded state
34
+ * - JG_AGENT_HQ_TARGET_FORBIDDEN: The HQ target is outside the allowed workspaces
35
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
36
+ * - JG_AGENT_HQ_CODEX_FAILED: The Codex integration failed
37
+ * - JG_AGENT_HQ_CLAUDE_FAILED: The Claude integration failed
38
38
  */
39
39
  quiescePlan(controls: IPlanRevisionWorkflow["controls"]): Promise<void>;
40
40
  /**
41
41
  * Stops admission, interrupts active work, cleans native terminals and confirms shutdown.
42
42
  * @returns A promise resolved after native exit and release of durable control ownership.
43
43
  * @throws
44
- * - -1: The Codex integration failed
45
- * - -1: The Claude integration failed
46
- * - -1: The HQ transport failed
47
- * - -1: The HQ store failed
44
+ * - JG_AGENT_HQ_CODEX_FAILED: The Codex integration failed
45
+ * - JG_AGENT_HQ_CLAUDE_FAILED: The Claude integration failed
46
+ * - JG_AGENT_HQ_TRANSPORT_FAILED: The HQ transport failed
47
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
48
48
  */
49
49
  close(): Promise<void>;
50
50
  };
@@ -1,5 +1,5 @@
1
1
  import { createHmac, randomUUID } from 'node:crypto';
2
- import { Exception, extractMessage } from 'error-message-utils';
2
+ import { Exception, extractMessage, isException } from 'jg-exception';
3
3
  import { z } from 'zod';
4
4
  import { setTimeout as wait } from 'node:timers/promises';
5
5
  import { getErrorKind } from '../errors/index.js';
@@ -40,12 +40,18 @@ export const createActions = (native, claude, readClaudeThread, store, roots, ep
40
40
  let unavailable = false;
41
41
  let eventFailure;
42
42
  const conflict = () => {
43
- throw new Exception('The HQ action conflicts with its recorded state', undefined, {
44
- kind: 'state-conflict',
43
+ throw new Exception('The HQ action conflicts with its recorded state', {
44
+ code: 'JG_AGENT_HQ_ACTION_CONFLICT',
45
+ data: {
46
+ kind: 'state-conflict',
47
+ },
45
48
  });
46
49
  };
47
50
  const agentUnavailable = () => {
48
- throw new Exception('The Codex integration failed', undefined, { kind: 'agent-unavailable' });
51
+ throw new Exception('The Codex integration failed', {
52
+ code: 'JG_AGENT_HQ_CODEX_FAILED',
53
+ data: { kind: 'agent-unavailable' },
54
+ });
49
55
  };
50
56
  /** Quarantines controls after a lifecycle gap and stops the native transport. */
51
57
  const failEvents = (cause) => {
@@ -111,7 +117,10 @@ export const createActions = (native, claude, readClaudeThread, store, roots, ep
111
117
  if (unavailable)
112
118
  return;
113
119
  if (eventCount >= 128) {
114
- failEvents(new Exception('The Codex integration failed', undefined, { kind: 'agent-unavailable' }));
120
+ failEvents(new Exception('The Codex integration failed', {
121
+ code: 'JG_AGENT_HQ_CODEX_FAILED',
122
+ data: { kind: 'agent-unavailable' },
123
+ }));
115
124
  return;
116
125
  }
117
126
  eventCount += 1;
@@ -179,8 +188,8 @@ export const createActions = (native, claude, readClaudeThread, store, roots, ep
179
188
  * Dispatches one admitted intent once and records delivery without replaying it.
180
189
  * @returns A promise resolved after delivery evidence or uncertainty is persisted.
181
190
  * @throws
182
- * - -1: The HQ action conflicts with its recorded state
183
- * - -1: The HQ store failed
191
+ * - JG_AGENT_HQ_ACTION_CONFLICT: The HQ action conflicts with its recorded state
192
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
184
193
  */
185
194
  const dispatch = async (initial, originalThread, command, input) => {
186
195
  let receipt = initial;
@@ -295,7 +304,7 @@ export const createActions = (native, claude, readClaudeThread, store, roots, ep
295
304
  }
296
305
  catch (error) {
297
306
  // Only a matching native error response proves rejection. Transport loss and malformed results remain uncertain.
298
- const explicit = error instanceof Exception && NativeRejectionSchema.safeParse(error.data).success;
307
+ const explicit = isException(error) && NativeRejectionSchema.safeParse(error.data).success;
299
308
  const kind = getErrorKind(error);
300
309
  const failure = kind === 'state-conflict' || kind === 'access-denied' || kind === 'agent-unavailable'
301
310
  ? kind
@@ -620,14 +629,14 @@ export const createActions = (native, claude, readClaudeThread, store, roots, ep
620
629
  * Validates the current target and separates new dispatch from idempotent receipt reads.
621
630
  * @returns The durable admission receipt; native work runs independently.
622
631
  * @throws
623
- * - -1: The HQ action conflicts with its recorded state
624
- * - -1: The HQ skill is unavailable
625
- * - -1: The HQ request is invalid
626
- * - -1: The HQ target is outside the allowed workspaces
627
- * - -1: The Codex integration failed
628
- * - -1: The Claude integration failed
629
- * - -1: The Claude history is unavailable
630
- * - -1: The HQ store failed
632
+ * - JG_AGENT_HQ_ACTION_CONFLICT: The HQ action conflicts with its recorded state
633
+ * - JG_AGENT_HQ_SKILL_UNAVAILABLE: The HQ skill is unavailable
634
+ * - JG_AGENT_HQ_REQUEST_INVALID: The HQ request is invalid
635
+ * - JG_AGENT_HQ_TARGET_FORBIDDEN: The HQ target is outside the allowed workspaces
636
+ * - JG_AGENT_HQ_CODEX_FAILED: The Codex integration failed
637
+ * - JG_AGENT_HQ_CLAUDE_FAILED: The Claude integration failed
638
+ * - JG_AGENT_HQ_CLAUDE_HISTORY_UNAVAILABLE: The Claude history is unavailable
639
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
631
640
  */
632
641
  const admitAndDispatch = async (action) => {
633
642
  // The worker verifies the complete digest before returning any existing receipt.
@@ -690,9 +699,15 @@ export const createActions = (native, claude, readClaudeThread, store, roots, ep
690
699
  const target = provider === 'codex'
691
700
  ? await native.readThread(nativeId)
692
701
  : await readClaudeThread(nativeId, thread.workspaceRoot);
693
- if ((provider === 'codex'
694
- ? target.nativeState !== 'notLoaded'
695
- : target.historyState !== 'available') ||
702
+ // native unsubscribe may retain an idle thread after its managed control is closed
703
+ const canResume = provider === 'codex'
704
+ ? target.nativeState === 'notLoaded' ||
705
+ (command.kind === 'thread-resume' &&
706
+ thread.state === 'inactive' &&
707
+ thread.ownership === 'managed' &&
708
+ target.nativeState === 'idle')
709
+ : target.historyState === 'available';
710
+ if (!canResume ||
696
711
  (await resolveWorkspaceTarget(target.workspaceRoot, roots)) !== thread.workspaceRoot)
697
712
  conflict();
698
713
  prepared.nativeId = nativeId;
@@ -804,14 +819,14 @@ export const createActions = (native, claude, readClaudeThread, store, roots, ep
804
819
  * Returns after durable admission; the native operation outlives this client request.
805
820
  * @returns The durable admission receipt after validation.
806
821
  * @throws
807
- * - -1: The HQ action conflicts with its recorded state
808
- * - -1: The HQ skill is unavailable
809
- * - -1: The HQ request is invalid
810
- * - -1: The HQ target is outside the allowed workspaces
811
- * - -1: The Codex integration failed
812
- * - -1: The Claude integration failed
813
- * - -1: The Claude history is unavailable
814
- * - -1: The HQ store failed
822
+ * - JG_AGENT_HQ_ACTION_CONFLICT: The HQ action conflicts with its recorded state
823
+ * - JG_AGENT_HQ_SKILL_UNAVAILABLE: The HQ skill is unavailable
824
+ * - JG_AGENT_HQ_REQUEST_INVALID: The HQ request is invalid
825
+ * - JG_AGENT_HQ_TARGET_FORBIDDEN: The HQ target is outside the allowed workspaces
826
+ * - JG_AGENT_HQ_CODEX_FAILED: The Codex integration failed
827
+ * - JG_AGENT_HQ_CLAUDE_FAILED: The Claude integration failed
828
+ * - JG_AGENT_HQ_CLAUDE_HISTORY_UNAVAILABLE: The Claude history is unavailable
829
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
815
830
  */
816
831
  async submit(action) {
817
832
  const admission = admitAndDispatch(action);
@@ -833,11 +848,11 @@ export const createActions = (native, claude, readClaudeThread, store, roots, ep
833
848
  * Quiesces the selected managed cohort without stopping unrelated controllers.
834
849
  * @returns A promise resolved only after interruption, background cleanup and release evidence.
835
850
  * @throws
836
- * - -1: The HQ action conflicts with its recorded state
837
- * - -1: The HQ target is outside the allowed workspaces
838
- * - -1: The HQ store failed
839
- * - -1: The Codex integration failed
840
- * - -1: The Claude integration failed
851
+ * - JG_AGENT_HQ_ACTION_CONFLICT: The HQ action conflicts with its recorded state
852
+ * - JG_AGENT_HQ_TARGET_FORBIDDEN: The HQ target is outside the allowed workspaces
853
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
854
+ * - JG_AGENT_HQ_CODEX_FAILED: The Codex integration failed
855
+ * - JG_AGENT_HQ_CLAUDE_FAILED: The Claude integration failed
841
856
  */
842
857
  async quiescePlan(controls) {
843
858
  const failures = [];
@@ -854,10 +869,10 @@ export const createActions = (native, claude, readClaudeThread, store, roots, ep
854
869
  * Stops admission, interrupts active work, cleans native terminals and confirms shutdown.
855
870
  * @returns A promise resolved after native exit and release of durable control ownership.
856
871
  * @throws
857
- * - -1: The Codex integration failed
858
- * - -1: The Claude integration failed
859
- * - -1: The HQ transport failed
860
- * - -1: The HQ store failed
872
+ * - JG_AGENT_HQ_CODEX_FAILED: The Codex integration failed
873
+ * - JG_AGENT_HQ_CLAUDE_FAILED: The Claude integration failed
874
+ * - JG_AGENT_HQ_TRANSPORT_FAILED: The HQ transport failed
875
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
861
876
  */
862
877
  async close() {
863
878
  closing = true;
@@ -896,7 +911,11 @@ export const createActions = (native, claude, readClaudeThread, store, roots, ep
896
911
  await lane.catch(() => undefined);
897
912
  const failedExit = nativeExit.find((result) => result.status === 'rejected');
898
913
  if (failedExit?.status === 'rejected')
899
- throw new Exception(`The HQ transport failed: ${extractMessage(failedExit.reason)}`, undefined, { kind: 'operation-failed' }, { cause: cleanupFailure ?? failedExit.reason });
914
+ throw new Exception(`The HQ transport failed: ${extractMessage(failedExit.reason)}`, {
915
+ code: 'JG_AGENT_HQ_TRANSPORT_FAILED',
916
+ data: { kind: 'operation-failed' },
917
+ cause: cleanupFailure ?? failedExit.reason,
918
+ });
900
919
  }
901
920
  if (cleanupFailure)
902
921
  throw cleanupFailure;
@@ -8,10 +8,10 @@ type IControlRead = Extract<IHqRequest, {
8
8
  * Authorizes bounded control reads and binds seek positions to their exact query.
9
9
  * @returns The selected record or bounded page.
10
10
  * @throws
11
- * - -1: The HQ action conflicts with its recorded state
12
- * - -1: The HQ cursor does not match this request
13
- * - -1: The HQ target is outside the allowed workspaces
14
- * - -1: The HQ store failed
11
+ * - JG_AGENT_HQ_ACTION_CONFLICT: The HQ action conflicts with its recorded state
12
+ * - JG_AGENT_HQ_CURSOR_INVALID: The HQ cursor does not match this request
13
+ * - JG_AGENT_HQ_TARGET_FORBIDDEN: The HQ target is outside the allowed workspaces
14
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
15
15
  */
16
16
  export declare const readControl: (command: IControlRead, store: IStore, actions: ReturnType<typeof createActions>, roots: string[]) => Promise<IHqResponse>;
17
17
  export {};
@@ -1,4 +1,4 @@
1
- import { Exception } from 'error-message-utils';
1
+ import { Exception } from 'jg-exception';
2
2
  import { z } from 'zod';
3
3
  import { resolveWorkspaceTarget } from '../runtime/state/index.js';
4
4
  import { decodeHistoryCursor, encodeHistoryCursor } from '../sessions/index.js';
@@ -6,7 +6,7 @@ const PositionSchema = z.strictObject({ createdAt: z.number().int(), id: z.uuid(
6
6
  /**
7
7
  * Validates the application-owned seek position after the shared query-binding check.
8
8
  * @throws
9
- * - -1: The HQ cursor does not match this request
9
+ * - JG_AGENT_HQ_CURSOR_INVALID: The HQ cursor does not match this request
10
10
  */
11
11
  const readPosition = (cursor, filters) => {
12
12
  const decoded = decodeHistoryCursor(cursor, filters);
@@ -16,7 +16,11 @@ const readPosition = (cursor, filters) => {
16
16
  return PositionSchema.parse(JSON.parse(decoded));
17
17
  }
18
18
  catch (cause) {
19
- throw new Exception('The HQ cursor does not match this request', undefined, { kind: 'invalid-request' }, { cause });
19
+ throw new Exception('The HQ cursor does not match this request', {
20
+ code: 'JG_AGENT_HQ_CURSOR_INVALID',
21
+ data: { kind: 'invalid-request' },
22
+ cause,
23
+ });
20
24
  }
21
25
  };
22
26
  /** Encodes the final row's complete ordering tuple only when another page exists. */
@@ -28,10 +32,10 @@ const nextPageCursor = (page, filters) => {
28
32
  * Authorizes bounded control reads and binds seek positions to their exact query.
29
33
  * @returns The selected record or bounded page.
30
34
  * @throws
31
- * - -1: The HQ action conflicts with its recorded state
32
- * - -1: The HQ cursor does not match this request
33
- * - -1: The HQ target is outside the allowed workspaces
34
- * - -1: The HQ store failed
35
+ * - JG_AGENT_HQ_ACTION_CONFLICT: The HQ action conflicts with its recorded state
36
+ * - JG_AGENT_HQ_CURSOR_INVALID: The HQ cursor does not match this request
37
+ * - JG_AGENT_HQ_TARGET_FORBIDDEN: The HQ target is outside the allowed workspaces
38
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
35
39
  */
36
40
  export const readControl = async (command, store, actions, roots) => {
37
41
  const authorize = async (thread) => {
@@ -80,8 +84,11 @@ export const readControl = async (command, store, actions, roots) => {
80
84
  if (command.kind === 'managed-thread')
81
85
  return { kind: command.kind, result: thread };
82
86
  if (!thread)
83
- throw new Exception('The HQ action conflicts with its recorded state', undefined, {
84
- kind: 'state-conflict',
87
+ throw new Exception('The HQ action conflicts with its recorded state', {
88
+ code: 'JG_AGENT_HQ_ACTION_CONFLICT',
89
+ data: {
90
+ kind: 'state-conflict',
91
+ },
85
92
  });
86
93
  if (command.kind === 'events')
87
94
  return {
package/dist/cli/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { resolve } from 'node:path';
3
3
  import { parseArgs } from 'node:util';
4
4
  import { setTimeout as wait } from 'node:timers/promises';
5
- import { Exception, hasErrorCode } from 'error-message-utils';
5
+ import { Exception, hasErrorCode, isException } from 'jg-exception';
6
6
  import { HqRequestSchema } from '../contracts/index.js';
7
7
  import { toPublicError } from '../errors/index.js';
8
8
  import { requestAgentHq } from '../ipc/index.js';
@@ -40,20 +40,32 @@ const main = async () => {
40
40
  return;
41
41
  }
42
42
  if (positionals.length !== 1 || !values['state-root'])
43
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' });
43
+ throw new Exception('The HQ request is invalid', {
44
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
45
+ data: { kind: 'invalid-request' },
46
+ });
44
47
  const stateRoot = resolve(values['state-root']);
45
48
  const roots = (values['workspace-root'] ?? []).map((root) => resolve(root));
46
49
  if (kind === 'request') {
47
50
  if (Object.keys(values).some((key) => key !== 'state-root'))
48
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' });
51
+ throw new Exception('The HQ request is invalid', {
52
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
53
+ data: { kind: 'invalid-request' },
54
+ });
49
55
  const chunks = [];
50
56
  let bytes = 0;
51
57
  for await (const chunk of process.stdin) {
52
58
  if (!Buffer.isBuffer(chunk))
53
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' });
59
+ throw new Exception('The HQ request is invalid', {
60
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
61
+ data: { kind: 'invalid-request' },
62
+ });
54
63
  bytes += chunk.length;
55
64
  if (bytes > 65_536)
56
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' });
65
+ throw new Exception('The HQ request is invalid', {
66
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
67
+ data: { kind: 'invalid-request' },
68
+ });
57
69
  chunks.push(chunk);
58
70
  }
59
71
  let input;
@@ -61,11 +73,19 @@ const main = async () => {
61
73
  input = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(Buffer.concat(chunks, bytes)));
62
74
  }
63
75
  catch (cause) {
64
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' }, { cause });
76
+ throw new Exception('The HQ request is invalid', {
77
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
78
+ data: { kind: 'invalid-request' },
79
+ cause,
80
+ });
65
81
  }
66
82
  const parsed = HqRequestSchema.safeParse(input);
67
83
  if (!parsed.success)
68
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' }, { cause: parsed.error });
84
+ throw new Exception('The HQ request is invalid', {
85
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
86
+ data: { kind: 'invalid-request' },
87
+ cause: parsed.error,
88
+ });
69
89
  process.stdout.write(`${JSON.stringify({ schemaVersion: 1, ...(await requestAgentHq(stateRoot, parsed.data)) })}\n`);
70
90
  return;
71
91
  }
@@ -83,7 +103,10 @@ const main = async () => {
83
103
  values.limit ||
84
104
  values.search ||
85
105
  values.provider)
86
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' });
106
+ throw new Exception('The HQ request is invalid', {
107
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
108
+ data: { kind: 'invalid-request' },
109
+ });
87
110
  const runtime = await createAgentHq({
88
111
  stateRoot,
89
112
  coordinationRoot: values['coordination-root']
@@ -117,9 +140,15 @@ const main = async () => {
117
140
  if (values['codex-extra-skill-root'] ||
118
141
  values['claude-additional-directory'] ||
119
142
  values['coordination-root'])
120
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' });
143
+ throw new Exception('The HQ request is invalid', {
144
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
145
+ data: { kind: 'invalid-request' },
146
+ });
121
147
  if (roots.length > 1)
122
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' });
148
+ throw new Exception('The HQ request is invalid', {
149
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
150
+ data: { kind: 'invalid-request' },
151
+ });
123
152
  if (kind === 'plan-status' || kind === 'plan-milestones') {
124
153
  const allowed = new Set([
125
154
  'state-root',
@@ -127,7 +156,10 @@ const main = async () => {
127
156
  ...(kind === 'plan-milestones' ? ['limit', 'cursor'] : []),
128
157
  ]);
129
158
  if (!values.directory || Object.keys(values).some((key) => !allowed.has(key)))
130
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' });
159
+ throw new Exception('The HQ request is invalid', {
160
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
161
+ data: { kind: 'invalid-request' },
162
+ });
131
163
  const parsed = HqRequestSchema.safeParse({
132
164
  kind: 'plan',
133
165
  command: {
@@ -139,7 +171,11 @@ const main = async () => {
139
171
  },
140
172
  });
141
173
  if (!parsed.success)
142
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' }, { cause: parsed.error });
174
+ throw new Exception('The HQ request is invalid', {
175
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
176
+ data: { kind: 'invalid-request' },
177
+ cause: parsed.error,
178
+ });
143
179
  process.stdout.write(`${JSON.stringify({ schemaVersion: 1, ...(await requestAgentHq(stateRoot, parsed.data)) })}\n`);
144
180
  return;
145
181
  }
@@ -149,7 +185,10 @@ const main = async () => {
149
185
  ...(kind === 'handoff-show' ? ['id'] : ['workspace-root', 'thread-id', 'limit', 'cursor']),
150
186
  ]);
151
187
  if (Object.keys(values).some((key) => !allowed.has(key)))
152
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' });
188
+ throw new Exception('The HQ request is invalid', {
189
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
190
+ data: { kind: 'invalid-request' },
191
+ });
153
192
  const parsed = HqRequestSchema.safeParse({
154
193
  kind: 'handoff',
155
194
  command: kind === 'handoff-show'
@@ -163,14 +202,24 @@ const main = async () => {
163
202
  },
164
203
  });
165
204
  if (!parsed.success)
166
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' }, { cause: parsed.error });
205
+ throw new Exception('The HQ request is invalid', {
206
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
207
+ data: { kind: 'invalid-request' },
208
+ cause: parsed.error,
209
+ });
167
210
  process.stdout.write(`${JSON.stringify({ schemaVersion: 1, ...(await requestAgentHq(stateRoot, parsed.data)) })}\n`);
168
211
  return;
169
212
  }
170
213
  if (values.id)
171
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' });
214
+ throw new Exception('The HQ request is invalid', {
215
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
216
+ data: { kind: 'invalid-request' },
217
+ });
172
218
  if (values.directory)
173
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' });
219
+ throw new Exception('The HQ request is invalid', {
220
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
221
+ data: { kind: 'invalid-request' },
222
+ });
174
223
  const payload = Object.fromEntries([
175
224
  ['kind', kind],
176
225
  [
@@ -193,7 +242,11 @@ const main = async () => {
193
242
  ].filter(([, entry]) => entry !== undefined));
194
243
  const parsed = HqRequestSchema.safeParse(payload);
195
244
  if (!parsed.success)
196
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' }, { cause: parsed.error });
245
+ throw new Exception('The HQ request is invalid', {
246
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
247
+ data: { kind: 'invalid-request' },
248
+ cause: parsed.error,
249
+ });
197
250
  const command = parsed.data;
198
251
  const response = await requestAgentHq(stateRoot, command);
199
252
  if (response.kind === 'stop') {
@@ -203,12 +256,15 @@ const main = async () => {
203
256
  try {
204
257
  const { owner } = await readRuntimeOwner(stateRoot);
205
258
  if (owner.nonce !== response.result.runtimeEpoch)
206
- throw new Exception('The HQ runtime owner is unverifiable', undefined, {
207
- kind: 'state-conflict',
259
+ throw new Exception('The HQ runtime owner is unverifiable', {
260
+ code: 'JG_AGENT_HQ_RUNTIME_OWNER_UNVERIFIABLE',
261
+ data: {
262
+ kind: 'state-conflict',
263
+ },
208
264
  });
209
265
  }
210
266
  catch (error) {
211
- if (error instanceof Exception && hasErrorCode(error.cause, 'ENOENT')) {
267
+ if (isException(error) && hasErrorCode(error.cause, 'ENOENT')) {
212
268
  stopped = true;
213
269
  break;
214
270
  }
@@ -217,7 +273,10 @@ const main = async () => {
217
273
  await wait(100);
218
274
  }
219
275
  if (!stopped)
220
- throw new Exception('The HQ transport failed', undefined, { kind: 'state-conflict' });
276
+ throw new Exception('The HQ transport failed', {
277
+ code: 'JG_AGENT_HQ_TRANSPORT_FAILED',
278
+ data: { kind: 'state-conflict' },
279
+ });
221
280
  process.stdout.write(`${JSON.stringify({ schemaVersion: 1, kind: 'stop', result: { state: 'stopped', runtimeEpoch: response.result.runtimeEpoch } })}\n`);
222
281
  }
223
282
  else
@@ -1,5 +1,14 @@
1
1
  export declare const DEFAULT_PAGE_SIZE = 32;
2
2
  export declare const MAX_PAGE_SIZE = 128;
3
+ export declare const PUBLIC_ERROR_CODES: {
4
+ readonly 'invalid-request': "JG_AGENT_HQ_CLIENT_INVALID_REQUEST";
5
+ readonly 'authentication-required': "JG_AGENT_HQ_CLIENT_AUTHENTICATION_REQUIRED";
6
+ readonly 'access-denied': "JG_AGENT_HQ_CLIENT_ACCESS_DENIED";
7
+ readonly 'not-found': "JG_AGENT_HQ_CLIENT_NOT_FOUND";
8
+ readonly 'state-conflict': "JG_AGENT_HQ_CLIENT_STATE_CONFLICT";
9
+ readonly 'agent-unavailable': "JG_AGENT_HQ_CLIENT_AGENT_UNAVAILABLE";
10
+ readonly 'operation-failed': "JG_AGENT_HQ_CLIENT_OPERATION_FAILED";
11
+ };
3
12
  export declare const PUBLIC_ERROR_MESSAGES: {
4
13
  readonly 'invalid-request': "Request is invalid.";
5
14
  readonly 'authentication-required': "Sign in to continue.";
@@ -1,6 +1,16 @@
1
1
  // trusted-boundary page sizes for growing collections
2
2
  export const DEFAULT_PAGE_SIZE = 32;
3
3
  export const MAX_PAGE_SIZE = 128;
4
+ // stable client codes for failures translated at IPC and browser boundaries
5
+ export const PUBLIC_ERROR_CODES = {
6
+ 'invalid-request': 'JG_AGENT_HQ_CLIENT_INVALID_REQUEST',
7
+ 'authentication-required': 'JG_AGENT_HQ_CLIENT_AUTHENTICATION_REQUIRED',
8
+ 'access-denied': 'JG_AGENT_HQ_CLIENT_ACCESS_DENIED',
9
+ 'not-found': 'JG_AGENT_HQ_CLIENT_NOT_FOUND',
10
+ 'state-conflict': 'JG_AGENT_HQ_CLIENT_STATE_CONFLICT',
11
+ 'agent-unavailable': 'JG_AGENT_HQ_CLIENT_AGENT_UNAVAILABLE',
12
+ 'operation-failed': 'JG_AGENT_HQ_CLIENT_OPERATION_FAILED',
13
+ };
4
14
  // safe messages at IPC and browser boundaries
5
15
  export const PUBLIC_ERROR_MESSAGES = {
6
16
  'invalid-request': 'Request is invalid.',