jg-agent-hq 0.5.2 → 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 (101) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +2 -0
  3. package/api/README.md +2 -0
  4. package/dist/actions/actions.d.ts +17 -17
  5. package/dist/actions/actions.js +47 -34
  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 +27 -14
  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 +30 -13
  55. package/dist/providers/claude/history/utilities.d.ts +3 -3
  56. package/dist/providers/claude/history/utilities.js +34 -16
  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/codex/approvals/utilities.d.ts +1 -1
  62. package/dist/providers/codex/approvals/utilities.js +6 -3
  63. package/dist/providers/codex/codex.d.ts +2 -2
  64. package/dist/providers/codex/codex.js +48 -21
  65. package/dist/providers/codex/rpc.d.ts +2 -2
  66. package/dist/providers/codex/rpc.js +60 -19
  67. package/dist/providers/codex/transformers.d.ts +4 -4
  68. package/dist/providers/codex/transformers.js +4 -4
  69. package/dist/providers/codex/types.d.ts +12 -12
  70. package/dist/providers/codex/validations.d.ts +1 -1
  71. package/dist/providers/codex/validations.js +7 -3
  72. package/dist/providers/process.d.ts +1 -1
  73. package/dist/providers/process.js +16 -7
  74. package/dist/providers/types.d.ts +3 -3
  75. package/dist/runtime/browser/browser.js +14 -5
  76. package/dist/runtime/runtime.d.ts +13 -13
  77. package/dist/runtime/runtime.js +49 -24
  78. package/dist/runtime/state/state.d.ts +18 -18
  79. package/dist/runtime/state/state.js +100 -45
  80. package/dist/runtime/types.d.ts +37 -37
  81. package/dist/sessions/sessions.d.ts +4 -4
  82. package/dist/sessions/sessions.js +15 -9
  83. package/dist/sessions/utilities.d.ts +1 -1
  84. package/dist/sessions/utilities.js +16 -6
  85. package/dist/skills/skills.d.ts +10 -10
  86. package/dist/skills/skills.js +27 -17
  87. package/dist/store/browser/browser.model.d.ts +1 -1
  88. package/dist/store/browser/browser.model.js +7 -4
  89. package/dist/store/control/control.model.d.ts +4 -4
  90. package/dist/store/control/control.model.js +10 -7
  91. package/dist/store/handoffs/handoffs.model.d.ts +3 -3
  92. package/dist/store/handoffs/handoffs.model.js +9 -6
  93. package/dist/store/plans/plans.model.d.ts +3 -3
  94. package/dist/store/plans/plans.model.js +29 -14
  95. package/dist/store/store.d.ts +1 -1
  96. package/dist/store/store.js +42 -13
  97. package/dist/store/store.worker.js +8 -4
  98. package/dist/store/types.d.ts +52 -51
  99. package/dist/store/types.js +3 -1
  100. package/errors/README.md +62 -62
  101. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
1
  import { execFile } from 'node:child_process';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { promisify } from 'node:util';
4
- import { Exception, extractMessage } from 'error-message-utils';
4
+ import { Exception, extractMessage } from 'jg-exception';
5
5
  import { z } from 'zod';
6
6
  import { HandoffPreviewSchema, } from '../contracts/index.js';
7
7
  import { resolveWorkspaceTarget } from '../runtime/state/index.js';
@@ -10,8 +10,11 @@ import { hashHandoff, handoffArguments, handoffDelivery } from './utilities.js';
10
10
  const run = promisify(execFile);
11
11
  const PositionSchema = z.strictObject({ createdAt: z.number().int(), id: z.uuid() });
12
12
  const conflict = () => {
13
- throw new Exception('The HQ action conflicts with its recorded state', undefined, {
14
- kind: 'state-conflict',
13
+ throw new Exception('The HQ action conflicts with its recorded state', {
14
+ code: 'JG_AGENT_HQ_ACTION_CONFLICT',
15
+ data: {
16
+ kind: 'state-conflict',
17
+ },
15
18
  });
16
19
  };
17
20
  /**
@@ -23,7 +26,7 @@ export const createHandoffs = (store, actions, skills, plans, readHistory, roots
23
26
  * Checks selected Git attribution without staging files or traversing history.
24
27
  * @returns Whether the selected commit is still HEAD; omitted attribution requires no Git read.
25
28
  * @throws
26
- * - -1: The HQ handoff operation failed
29
+ * - JG_AGENT_HQ_HANDOFF_FAILED: The HQ handoff operation failed
27
30
  */
28
31
  const checkCommit = async (directory, expected) => {
29
32
  if (!expected)
@@ -38,21 +41,28 @@ export const createHandoffs = (store, actions, skills, plans, readHistory, roots
38
41
  return result.stdout.trim() === expected;
39
42
  }
40
43
  catch (cause) {
41
- throw new Exception(`The HQ handoff operation failed: ${extractMessage(cause)}`, undefined, { kind: 'operation-failed' }, { cause });
44
+ throw new Exception(`The HQ handoff operation failed: ${extractMessage(cause)}`, {
45
+ code: 'JG_AGENT_HQ_HANDOFF_FAILED',
46
+ data: { kind: 'operation-failed' },
47
+ cause,
48
+ });
42
49
  }
43
50
  };
44
51
  /**
45
52
  * Reads and authorizes a selected record before exposing its retained evidence.
46
53
  * @returns The bounded persisted handoff.
47
54
  * @throws
48
- * - -1: The HQ handoff is unavailable
49
- * - -1: The HQ target is outside the allowed workspaces
50
- * - -1: The HQ store failed
55
+ * - JG_AGENT_HQ_HANDOFF_UNAVAILABLE: The HQ handoff is unavailable
56
+ * - JG_AGENT_HQ_TARGET_FORBIDDEN: The HQ target is outside the allowed workspaces
57
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
51
58
  */
52
59
  const selected = async (id) => {
53
60
  const record = await store.getHandoff(id);
54
61
  if (!record)
55
- throw new Exception('The HQ handoff is unavailable', undefined, { kind: 'not-found' });
62
+ throw new Exception('The HQ handoff is unavailable', {
63
+ code: 'JG_AGENT_HQ_HANDOFF_UNAVAILABLE',
64
+ data: { kind: 'not-found' },
65
+ });
56
66
  await resolveWorkspaceTarget(record.source.workspaceRoot, roots);
57
67
  return record;
58
68
  };
@@ -60,7 +70,7 @@ export const createHandoffs = (store, actions, skills, plans, readHistory, roots
60
70
  * Resolves the original native creation receipt; discovery never creates another controller.
61
71
  * @returns The current control record, or null before creation admission.
62
72
  * @throws
63
- * - -1: The HQ store failed
73
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
64
74
  */
65
75
  const target = async (record) => {
66
76
  const creation = await store.getAction(record.creation.id);
@@ -70,7 +80,7 @@ export const createHandoffs = (store, actions, skills, plans, readHistory, roots
70
80
  * Projects delivery from the action ledger, with no mirrored acknowledgement state.
71
81
  * @returns Selected findings and current native receipts, preserving the original reviewer identity.
72
82
  * @throws
73
- * - -1: The HQ store failed
83
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
74
84
  */
75
85
  const show = async (record) => {
76
86
  const [creation, reviewDispatch, feedbackDispatch] = await Promise.all([
@@ -103,24 +113,24 @@ export const createHandoffs = (store, actions, skills, plans, readHistory, roots
103
113
  * Checks that feedback still concerns the selected repository and execution contracts.
104
114
  * @returns Whether source identity, selected commit and current contracts still match.
105
115
  * @throws
106
- * - -1: The HQ handoff operation failed
107
- * - -1: The HQ store failed
108
- * - -1: The HQ target is outside the allowed workspaces
109
- * - -1: The HQ plan is invalid
110
- * - -1: The HQ plan operation failed
111
- * - -1: The HQ lock acquisition arguments are invalid
112
- * - -1: The HQ lock requires Linux or macOS
113
- * - -1: The HQ runtime directory must be a real directory
114
- * - -1: The HQ runtime directory must be private and owned by the current user
115
- * - -1: The HQ lock must be a regular file
116
- * - -1: The HQ lock must be private and owned by the current user
117
- * - -1: The HQ lock owner exceeds the supported size
118
- * - -1: The HQ lock owner could not be read. Inspect .runtime/hq.lock
119
- * - -1: The HQ lock owner is incomplete. Inspect .runtime/hq.lock and remove it only after its owner stopped
120
- * - -1: The HQ lock has an inactive owner
121
- * - -1: The HQ lock owner could not be verified
122
- * - -1: Timed out waiting for the HQ lock
123
- * - -1: HQ lock ownership changed before release
116
+ * - JG_AGENT_HQ_HANDOFF_FAILED: The HQ handoff operation failed
117
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
118
+ * - JG_AGENT_HQ_TARGET_FORBIDDEN: The HQ target is outside the allowed workspaces
119
+ * - JG_AGENT_HQ_PLAN_INVALID: The HQ plan is invalid
120
+ * - JG_AGENT_HQ_PLAN_FAILED: The HQ plan operation failed
121
+ * - JG_AGENT_HQ_LOCK_INVALID_ARGUMENTS: The HQ lock acquisition arguments are invalid
122
+ * - JG_AGENT_HQ_LOCK_UNSUPPORTED_PLATFORM: The HQ lock requires Linux or macOS
123
+ * - JG_AGENT_HQ_RUNTIME_DIRECTORY_INVALID: The HQ runtime directory must be a real directory
124
+ * - JG_AGENT_HQ_RUNTIME_DIRECTORY_PERMISSIONS: The HQ runtime directory must be private and owned by the current user
125
+ * - JG_AGENT_HQ_LOCK_FILE_INVALID: The HQ lock must be a regular file
126
+ * - JG_AGENT_HQ_LOCK_FILE_PERMISSIONS: The HQ lock must be private and owned by the current user
127
+ * - JG_AGENT_HQ_LOCK_OWNER_OVERSIZED: The HQ lock owner exceeds the supported size
128
+ * - JG_AGENT_HQ_LOCK_OWNER_UNREADABLE: The HQ lock owner could not be read. Inspect .runtime/hq.lock
129
+ * - JG_AGENT_HQ_LOCK_OWNER_INCOMPLETE: The HQ lock owner is incomplete. Inspect .runtime/hq.lock and remove it only after its owner stopped
130
+ * - JG_AGENT_HQ_LOCK_OWNER_INACTIVE: The HQ lock has an inactive owner
131
+ * - JG_AGENT_HQ_LOCK_OWNER_UNVERIFIABLE: The HQ lock owner could not be verified
132
+ * - JG_AGENT_HQ_LOCK_TIMEOUT: Timed out waiting for the HQ lock
133
+ * - JG_AGENT_HQ_LOCK_OWNERSHIP_CHANGED: HQ lock ownership changed before release
124
134
  */
125
135
  const isSourceCurrent = async (record) => {
126
136
  const source = await store.getThread(record.source.threadId);
@@ -147,25 +157,25 @@ export const createHandoffs = (store, actions, skills, plans, readHistory, roots
147
157
  * Resolves native skill metadata and exact invocation text without sending it.
148
158
  * @returns A digest-bound preview with explicit unavailable states.
149
159
  * @throws
150
- * - -1: The HQ handoff operation failed
151
- * - -1: The HQ action conflicts with its recorded state
152
- * - -1: The HQ store failed
153
- * - -1: The HQ target is outside the allowed workspaces
154
- * - -1: The HQ plan is invalid
155
- * - -1: The HQ plan operation failed
156
- * - -1: The HQ lock acquisition arguments are invalid
157
- * - -1: The HQ lock requires Linux or macOS
158
- * - -1: The HQ runtime directory must be a real directory
159
- * - -1: The HQ runtime directory must be private and owned by the current user
160
- * - -1: The HQ lock must be a regular file
161
- * - -1: The HQ lock must be private and owned by the current user
162
- * - -1: The HQ lock owner exceeds the supported size
163
- * - -1: The HQ lock owner could not be read. Inspect .runtime/hq.lock
164
- * - -1: The HQ lock owner is incomplete. Inspect .runtime/hq.lock and remove it only after its owner stopped
165
- * - -1: The HQ lock has an inactive owner
166
- * - -1: The HQ lock owner could not be verified
167
- * - -1: Timed out waiting for the HQ lock
168
- * - -1: HQ lock ownership changed before release
160
+ * - JG_AGENT_HQ_HANDOFF_FAILED: The HQ handoff operation failed
161
+ * - JG_AGENT_HQ_ACTION_CONFLICT: The HQ action conflicts with its recorded state
162
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
163
+ * - JG_AGENT_HQ_TARGET_FORBIDDEN: The HQ target is outside the allowed workspaces
164
+ * - JG_AGENT_HQ_PLAN_INVALID: The HQ plan is invalid
165
+ * - JG_AGENT_HQ_PLAN_FAILED: The HQ plan operation failed
166
+ * - JG_AGENT_HQ_LOCK_INVALID_ARGUMENTS: The HQ lock acquisition arguments are invalid
167
+ * - JG_AGENT_HQ_LOCK_UNSUPPORTED_PLATFORM: The HQ lock requires Linux or macOS
168
+ * - JG_AGENT_HQ_RUNTIME_DIRECTORY_INVALID: The HQ runtime directory must be a real directory
169
+ * - JG_AGENT_HQ_RUNTIME_DIRECTORY_PERMISSIONS: The HQ runtime directory must be private and owned by the current user
170
+ * - JG_AGENT_HQ_LOCK_FILE_INVALID: The HQ lock must be a regular file
171
+ * - JG_AGENT_HQ_LOCK_FILE_PERMISSIONS: The HQ lock must be private and owned by the current user
172
+ * - JG_AGENT_HQ_LOCK_OWNER_OVERSIZED: The HQ lock owner exceeds the supported size
173
+ * - JG_AGENT_HQ_LOCK_OWNER_UNREADABLE: The HQ lock owner could not be read. Inspect .runtime/hq.lock
174
+ * - JG_AGENT_HQ_LOCK_OWNER_INCOMPLETE: The HQ lock owner is incomplete. Inspect .runtime/hq.lock and remove it only after its owner stopped
175
+ * - JG_AGENT_HQ_LOCK_OWNER_INACTIVE: The HQ lock has an inactive owner
176
+ * - JG_AGENT_HQ_LOCK_OWNER_UNVERIFIABLE: The HQ lock owner could not be verified
177
+ * - JG_AGENT_HQ_LOCK_TIMEOUT: Timed out waiting for the HQ lock
178
+ * - JG_AGENT_HQ_LOCK_OWNERSHIP_CHANGED: HQ lock ownership changed before release
169
179
  */
170
180
  const preview = async (record, phase) => {
171
181
  const reviewer = await target(record);
@@ -226,31 +236,31 @@ export const createHandoffs = (store, actions, skills, plans, readHistory, roots
226
236
  * Creates, previews, explicitly sends or collects one handoff without an automatic reply loop.
227
237
  * @returns A bounded handoff, native invocation preview or seek page.
228
238
  * @throws
229
- * - -1: The HQ handoff is unavailable
230
- * - -1: The HQ handoff operation failed
231
- * - -1: The HQ action conflicts with its recorded state
232
- * - -1: The HQ skill is unavailable
233
- * - -1: The HQ target is outside the allowed workspaces
234
- * - -1: The HQ cursor does not match this request
235
- * - -1: The HQ store failed
236
- * - -1: The Codex integration failed
237
- * - -1: The Claude integration failed
238
- * - -1: The Claude history is unavailable
239
- * - -1: The HQ plan is invalid
240
- * - -1: The HQ plan operation failed
241
- * - -1: The HQ lock acquisition arguments are invalid
242
- * - -1: The HQ lock requires Linux or macOS
243
- * - -1: The HQ runtime directory must be a real directory
244
- * - -1: The HQ runtime directory must be private and owned by the current user
245
- * - -1: The HQ lock must be a regular file
246
- * - -1: The HQ lock must be private and owned by the current user
247
- * - -1: The HQ lock owner exceeds the supported size
248
- * - -1: The HQ lock owner could not be read. Inspect .runtime/hq.lock
249
- * - -1: The HQ lock owner is incomplete. Inspect .runtime/hq.lock and remove it only after its owner stopped
250
- * - -1: The HQ lock has an inactive owner
251
- * - -1: The HQ lock owner could not be verified
252
- * - -1: Timed out waiting for the HQ lock
253
- * - -1: HQ lock ownership changed before release
239
+ * - JG_AGENT_HQ_HANDOFF_UNAVAILABLE: The HQ handoff is unavailable
240
+ * - JG_AGENT_HQ_HANDOFF_FAILED: The HQ handoff operation failed
241
+ * - JG_AGENT_HQ_ACTION_CONFLICT: The HQ action conflicts with its recorded state
242
+ * - JG_AGENT_HQ_SKILL_UNAVAILABLE: The HQ skill is unavailable
243
+ * - JG_AGENT_HQ_TARGET_FORBIDDEN: The HQ target is outside the allowed workspaces
244
+ * - JG_AGENT_HQ_CURSOR_INVALID: The HQ cursor does not match this request
245
+ * - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
246
+ * - JG_AGENT_HQ_CODEX_FAILED: The Codex integration failed
247
+ * - JG_AGENT_HQ_CLAUDE_FAILED: The Claude integration failed
248
+ * - JG_AGENT_HQ_CLAUDE_HISTORY_UNAVAILABLE: The Claude history is unavailable
249
+ * - JG_AGENT_HQ_PLAN_INVALID: The HQ plan is invalid
250
+ * - JG_AGENT_HQ_PLAN_FAILED: The HQ plan operation failed
251
+ * - JG_AGENT_HQ_LOCK_INVALID_ARGUMENTS: The HQ lock acquisition arguments are invalid
252
+ * - JG_AGENT_HQ_LOCK_UNSUPPORTED_PLATFORM: The HQ lock requires Linux or macOS
253
+ * - JG_AGENT_HQ_RUNTIME_DIRECTORY_INVALID: The HQ runtime directory must be a real directory
254
+ * - JG_AGENT_HQ_RUNTIME_DIRECTORY_PERMISSIONS: The HQ runtime directory must be private and owned by the current user
255
+ * - JG_AGENT_HQ_LOCK_FILE_INVALID: The HQ lock must be a regular file
256
+ * - JG_AGENT_HQ_LOCK_FILE_PERMISSIONS: The HQ lock must be private and owned by the current user
257
+ * - JG_AGENT_HQ_LOCK_OWNER_OVERSIZED: The HQ lock owner exceeds the supported size
258
+ * - JG_AGENT_HQ_LOCK_OWNER_UNREADABLE: The HQ lock owner could not be read. Inspect .runtime/hq.lock
259
+ * - JG_AGENT_HQ_LOCK_OWNER_INCOMPLETE: The HQ lock owner is incomplete. Inspect .runtime/hq.lock and remove it only after its owner stopped
260
+ * - JG_AGENT_HQ_LOCK_OWNER_INACTIVE: The HQ lock has an inactive owner
261
+ * - JG_AGENT_HQ_LOCK_OWNER_UNVERIFIABLE: The HQ lock owner could not be verified
262
+ * - JG_AGENT_HQ_LOCK_TIMEOUT: Timed out waiting for the HQ lock
263
+ * - JG_AGENT_HQ_LOCK_OWNERSHIP_CHANGED: HQ lock ownership changed before release
254
264
  */
255
265
  async handle(command) {
256
266
  if (command.kind === 'list') {
@@ -264,14 +274,20 @@ export const createHandoffs = (store, actions, skills, plans, readHistory, roots
264
274
  value = JSON.parse(continuation);
265
275
  }
266
276
  catch {
267
- throw new Exception('The HQ cursor does not match this request', undefined, {
268
- kind: 'invalid-request',
277
+ throw new Exception('The HQ cursor does not match this request', {
278
+ code: 'JG_AGENT_HQ_CURSOR_INVALID',
279
+ data: {
280
+ kind: 'invalid-request',
281
+ },
269
282
  });
270
283
  }
271
284
  const parsed = PositionSchema.safeParse(value);
272
285
  if (!parsed.success)
273
- throw new Exception('The HQ cursor does not match this request', undefined, {
274
- kind: 'invalid-request',
286
+ throw new Exception('The HQ cursor does not match this request', {
287
+ code: 'JG_AGENT_HQ_CURSOR_INVALID',
288
+ data: {
289
+ kind: 'invalid-request',
290
+ },
275
291
  });
276
292
  after = parsed.data;
277
293
  }
@@ -3,18 +3,18 @@ import { type IHqRequestInput, type IHqResponse } from '../contracts/index.js';
3
3
  * Runs one validated local command and closes only its client connection.
4
4
  * @returns The matching normalized response; stopping requires subsequent shutdown confirmation.
5
5
  * @throws
6
- * - -1: The HQ request is invalid
7
- * - -1: The HQ state directory is unsafe
8
- * - -1: The HQ state file is unsafe
9
- * - -1: The HQ runtime owner is unverifiable
10
- * - -1: The HQ runtime is not running
11
- * - -1: The HQ transport failed
12
- * - -1: Request is invalid.
13
- * - -1: Sign in to continue.
14
- * - -1: Access is denied.
15
- * - -1: Requested resource was not found.
16
- * - -1: State changed. Refresh before continuing.
17
- * - -1: Coding agent is unavailable.
18
- * - -1: Operation could not be completed.
6
+ * - JG_AGENT_HQ_REQUEST_INVALID: The HQ request is invalid
7
+ * - JG_AGENT_HQ_STATE_DIRECTORY_UNSAFE: The HQ state directory is unsafe
8
+ * - JG_AGENT_HQ_STATE_FILE_UNSAFE: The HQ state file is unsafe
9
+ * - JG_AGENT_HQ_RUNTIME_OWNER_UNVERIFIABLE: The HQ runtime owner is unverifiable
10
+ * - JG_AGENT_HQ_RUNTIME_NOT_RUNNING: The HQ runtime is not running
11
+ * - JG_AGENT_HQ_TRANSPORT_FAILED: The HQ transport failed
12
+ * - JG_AGENT_HQ_CLIENT_INVALID_REQUEST: Request is invalid.
13
+ * - JG_AGENT_HQ_CLIENT_AUTHENTICATION_REQUIRED: Sign in to continue.
14
+ * - JG_AGENT_HQ_CLIENT_ACCESS_DENIED: Access is denied.
15
+ * - JG_AGENT_HQ_CLIENT_NOT_FOUND: Requested resource was not found.
16
+ * - JG_AGENT_HQ_CLIENT_STATE_CONFLICT: State changed. Refresh before continuing.
17
+ * - JG_AGENT_HQ_CLIENT_AGENT_UNAVAILABLE: Coding agent is unavailable.
18
+ * - JG_AGENT_HQ_CLIENT_OPERATION_FAILED: Operation could not be completed.
19
19
  */
20
20
  export declare const requestAgentHq: (stateRoot: string, command: IHqRequestInput) => Promise<IHqResponse>;
@@ -2,8 +2,8 @@ import { randomUUID } from 'node:crypto';
2
2
  import { lstat } from 'node:fs/promises';
3
3
  import { createConnection } from 'node:net';
4
4
  import { isAbsolute, join } from 'node:path';
5
- import { Exception, extractMessage } from 'error-message-utils';
6
- import { HqRequestSchema, IpcResponseSchema, PUBLIC_ERROR_MESSAGES, } from '../contracts/index.js';
5
+ import { Exception, extractMessage } from 'jg-exception';
6
+ import { HqRequestSchema, IpcResponseSchema, PUBLIC_ERROR_CODES, PUBLIC_ERROR_MESSAGES, } from '../contracts/index.js';
7
7
  import { isPrivateOwner } from '../locks/index.js';
8
8
  import { readRuntimeOwner } from '../runtime/state/index.js';
9
9
  import { CONTROL_SOCKET, IPC_TIMEOUT_MS, MAX_FRAME_BYTES, MAX_REQUEST_BYTES } from './constants.js';
@@ -12,53 +12,78 @@ import { createLineDecoder } from './framing.js';
12
12
  * Runs one validated local command and closes only its client connection.
13
13
  * @returns The matching normalized response; stopping requires subsequent shutdown confirmation.
14
14
  * @throws
15
- * - -1: The HQ request is invalid
16
- * - -1: The HQ state directory is unsafe
17
- * - -1: The HQ state file is unsafe
18
- * - -1: The HQ runtime owner is unverifiable
19
- * - -1: The HQ runtime is not running
20
- * - -1: The HQ transport failed
21
- * - -1: Request is invalid.
22
- * - -1: Sign in to continue.
23
- * - -1: Access is denied.
24
- * - -1: Requested resource was not found.
25
- * - -1: State changed. Refresh before continuing.
26
- * - -1: Coding agent is unavailable.
27
- * - -1: Operation could not be completed.
15
+ * - JG_AGENT_HQ_REQUEST_INVALID: The HQ request is invalid
16
+ * - JG_AGENT_HQ_STATE_DIRECTORY_UNSAFE: The HQ state directory is unsafe
17
+ * - JG_AGENT_HQ_STATE_FILE_UNSAFE: The HQ state file is unsafe
18
+ * - JG_AGENT_HQ_RUNTIME_OWNER_UNVERIFIABLE: The HQ runtime owner is unverifiable
19
+ * - JG_AGENT_HQ_RUNTIME_NOT_RUNNING: The HQ runtime is not running
20
+ * - JG_AGENT_HQ_TRANSPORT_FAILED: The HQ transport failed
21
+ * - JG_AGENT_HQ_CLIENT_INVALID_REQUEST: Request is invalid.
22
+ * - JG_AGENT_HQ_CLIENT_AUTHENTICATION_REQUIRED: Sign in to continue.
23
+ * - JG_AGENT_HQ_CLIENT_ACCESS_DENIED: Access is denied.
24
+ * - JG_AGENT_HQ_CLIENT_NOT_FOUND: Requested resource was not found.
25
+ * - JG_AGENT_HQ_CLIENT_STATE_CONFLICT: State changed. Refresh before continuing.
26
+ * - JG_AGENT_HQ_CLIENT_AGENT_UNAVAILABLE: Coding agent is unavailable.
27
+ * - JG_AGENT_HQ_CLIENT_OPERATION_FAILED: Operation could not be completed.
28
28
  */
29
29
  export const requestAgentHq = async (stateRoot, command) => {
30
30
  const input = HqRequestSchema.safeParse(command);
31
31
  if (!input.success || !isAbsolute(stateRoot))
32
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' });
32
+ throw new Exception('The HQ request is invalid', {
33
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
34
+ data: { kind: 'invalid-request' },
35
+ });
33
36
  const { owner } = await readRuntimeOwner(stateRoot);
34
37
  const path = join(stateRoot, CONTROL_SOCKET);
35
38
  const socketDetails = await lstat(path);
36
39
  if (!socketDetails.isSocket() || socketDetails.isSymbolicLink() || !isPrivateOwner(socketDetails))
37
- throw new Exception('The HQ state file is unsafe', undefined, { kind: 'state-conflict' });
40
+ throw new Exception('The HQ state file is unsafe', {
41
+ code: 'JG_AGENT_HQ_STATE_FILE_UNSAFE',
42
+ data: { kind: 'state-conflict' },
43
+ });
38
44
  const id = randomUUID();
39
45
  const content = `${JSON.stringify({ schemaVersion: 1, id, runtimeEpoch: owner.nonce, command: input.data })}\n`;
40
46
  if (Buffer.byteLength(content) > MAX_REQUEST_BYTES)
41
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' });
47
+ throw new Exception('The HQ request is invalid', {
48
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
49
+ data: { kind: 'invalid-request' },
50
+ });
42
51
  const socket = createConnection(path);
43
52
  try {
44
53
  return await new Promise((resolve, reject) => {
45
- socket.setTimeout(IPC_TIMEOUT_MS, () => reject(new Exception('The HQ transport failed', undefined, { kind: 'agent-unavailable' })));
46
- socket.once('error', (cause) => reject(new Exception(`The HQ transport failed: ${extractMessage(cause)}`, undefined, { kind: 'agent-unavailable' }, { cause })));
54
+ socket.setTimeout(IPC_TIMEOUT_MS, () => reject(new Exception('The HQ transport failed', {
55
+ code: 'JG_AGENT_HQ_TRANSPORT_FAILED',
56
+ data: { kind: 'agent-unavailable' },
57
+ })));
58
+ socket.once('error', (cause) => reject(new Exception(`The HQ transport failed: ${extractMessage(cause)}`, {
59
+ code: 'JG_AGENT_HQ_TRANSPORT_FAILED',
60
+ data: { kind: 'agent-unavailable' },
61
+ cause,
62
+ })));
47
63
  let received = false;
48
64
  const decoder = createLineDecoder(MAX_FRAME_BYTES, (message) => {
49
65
  const parsed = IpcResponseSchema.safeParse(message);
50
66
  if (!parsed.success || parsed.data.id !== id)
51
- throw new Exception('The HQ transport failed', undefined, { kind: 'agent-unavailable' });
67
+ throw new Exception('The HQ transport failed', {
68
+ code: 'JG_AGENT_HQ_TRANSPORT_FAILED',
69
+ data: { kind: 'agent-unavailable' },
70
+ });
52
71
  received = true;
53
72
  const response = parsed.data;
54
73
  if (!response.success) {
55
- reject(new Exception(PUBLIC_ERROR_MESSAGES[response.error.kind], undefined, {
56
- kind: response.error.kind,
74
+ reject(new Exception(PUBLIC_ERROR_MESSAGES[response.error.kind], {
75
+ code: PUBLIC_ERROR_CODES[response.error.kind],
76
+ data: {
77
+ kind: response.error.kind,
78
+ },
57
79
  }));
58
80
  return;
59
81
  }
60
82
  if (response.response.kind !== input.data.kind)
61
- throw new Exception('The HQ transport failed', undefined, { kind: 'agent-unavailable' });
83
+ throw new Exception('The HQ transport failed', {
84
+ code: 'JG_AGENT_HQ_TRANSPORT_FAILED',
85
+ data: { kind: 'agent-unavailable' },
86
+ });
62
87
  resolve(response.response);
63
88
  });
64
89
  socket.on('data', (chunk) => {
@@ -73,7 +98,10 @@ export const requestAgentHq = async (stateRoot, command) => {
73
98
  try {
74
99
  decoder.end();
75
100
  if (!received)
76
- reject(new Exception('The HQ transport failed', undefined, { kind: 'agent-unavailable' }));
101
+ reject(new Exception('The HQ transport failed', {
102
+ code: 'JG_AGENT_HQ_TRANSPORT_FAILED',
103
+ data: { kind: 'agent-unavailable' },
104
+ }));
77
105
  }
78
106
  catch (error) {
79
107
  reject(error);
@@ -4,7 +4,7 @@
4
4
  * @param receive The owner of each decoded message.
5
5
  * @returns A decoder whose end operation rejects incomplete tails.
6
6
  * @throws
7
- * - -1: The HQ transport failed
7
+ * - JG_AGENT_HQ_TRANSPORT_FAILED: The HQ transport failed
8
8
  */
9
9
  export declare const createLineDecoder: (maxBytes: number, receive: (message: unknown) => void) => {
10
10
  push(chunk: Buffer): void;
@@ -1,11 +1,11 @@
1
- import { Exception } from 'error-message-utils';
1
+ import { Exception } from 'jg-exception';
2
2
  /**
3
3
  * Decodes bounded JSON lines with amortized linear copying across fragmented input.
4
4
  * @param maxBytes Maximum bytes in one JSON frame.
5
5
  * @param receive The owner of each decoded message.
6
6
  * @returns A decoder whose end operation rejects incomplete tails.
7
7
  * @throws
8
- * - -1: The HQ transport failed
8
+ * - JG_AGENT_HQ_TRANSPORT_FAILED: The HQ transport failed
9
9
  */
10
10
  export const createLineDecoder = (maxBytes, receive) => {
11
11
  let buffer = Buffer.alloc(Math.min(1024, maxBytes));
@@ -19,7 +19,10 @@ export const createLineDecoder = (maxBytes, receive) => {
19
19
  const end = newline < 0 ? chunk.length : newline;
20
20
  const length = end - offset;
21
21
  if (used + length > maxBytes)
22
- throw new Exception('The HQ transport failed', undefined, { kind: 'invalid-request' });
22
+ throw new Exception('The HQ transport failed', {
23
+ code: 'JG_AGENT_HQ_TRANSPORT_FAILED',
24
+ data: { kind: 'invalid-request' },
25
+ });
23
26
  if (used + length > buffer.length) {
24
27
  const expanded = Buffer.alloc(Math.min(maxBytes, Math.max(buffer.length * 2, used + length)));
25
28
  buffer.copy(expanded, 0, 0, used);
@@ -33,7 +36,11 @@ export const createLineDecoder = (maxBytes, receive) => {
33
36
  message = JSON.parse(decoder.decode(buffer.subarray(0, used)));
34
37
  }
35
38
  catch (cause) {
36
- throw new Exception('The HQ transport failed', undefined, { kind: 'invalid-request' }, { cause });
39
+ throw new Exception('The HQ transport failed', {
40
+ code: 'JG_AGENT_HQ_TRANSPORT_FAILED',
41
+ data: { kind: 'invalid-request' },
42
+ cause,
43
+ });
37
44
  }
38
45
  used = 0;
39
46
  receive(message);
@@ -43,7 +50,10 @@ export const createLineDecoder = (maxBytes, receive) => {
43
50
  },
44
51
  end() {
45
52
  if (used)
46
- throw new Exception('The HQ transport failed', undefined, { kind: 'invalid-request' });
53
+ throw new Exception('The HQ transport failed', {
54
+ code: 'JG_AGENT_HQ_TRANSPORT_FAILED',
55
+ data: { kind: 'invalid-request' },
56
+ });
47
57
  },
48
58
  };
49
59
  };
@@ -3,7 +3,7 @@ import { type IHqRequest, type IHqResponse } from '../contracts/index.js';
3
3
  * Serves closed HQ commands over an owner-only Unix socket. Disconnects do not own host lifetime.
4
4
  * @returns An owned server with deterministic connection teardown.
5
5
  * @throws
6
- * - -1: The HQ transport failed
6
+ * - JG_AGENT_HQ_TRANSPORT_FAILED: The HQ transport failed
7
7
  */
8
8
  export declare const createIpcServer: (path: string, runtimeEpoch: string, handle: (command: IHqRequest) => Promise<IHqResponse>) => Promise<{
9
9
  readonly isAvailable: boolean;
@@ -1,7 +1,7 @@
1
1
  import { createServer } from 'node:net';
2
2
  import { chmod } from 'node:fs/promises';
3
3
  import { randomUUID } from 'node:crypto';
4
- import { Exception, extractMessage } from 'error-message-utils';
4
+ import { Exception, extractMessage } from 'jg-exception';
5
5
  import { HqResponseSchema, IpcRequestSchema, PUBLIC_ERROR_MESSAGES, } from '../contracts/index.js';
6
6
  import { toPublicError } from '../errors/index.js';
7
7
  import { IPC_TIMEOUT_MS, MAX_CONNECTIONS, MAX_FRAME_BYTES, MAX_REQUEST_BYTES, } from './constants.js';
@@ -10,7 +10,7 @@ import { createLineDecoder } from './framing.js';
10
10
  * Serves closed HQ commands over an owner-only Unix socket. Disconnects do not own host lifetime.
11
11
  * @returns An owned server with deterministic connection teardown.
12
12
  * @throws
13
- * - -1: The HQ transport failed
13
+ * - JG_AGENT_HQ_TRANSPORT_FAILED: The HQ transport failed
14
14
  */
15
15
  export const createIpcServer = async (path, runtimeEpoch, handle) => {
16
16
  const sockets = new Set();
@@ -36,14 +36,24 @@ export const createIpcServer = async (path, runtimeEpoch, handle) => {
36
36
  const id = input.success ? input.data.id : randomUUID();
37
37
  try {
38
38
  if (!input.success)
39
- throw new Exception('The HQ request is invalid', undefined, { kind: 'invalid-request' }, { cause: input.error });
39
+ throw new Exception('The HQ request is invalid', {
40
+ code: 'JG_AGENT_HQ_REQUEST_INVALID',
41
+ data: { kind: 'invalid-request' },
42
+ cause: input.error,
43
+ });
40
44
  if (input.data.runtimeEpoch !== runtimeEpoch)
41
- throw new Exception('The HQ runtime owner is unverifiable', undefined, {
42
- kind: 'state-conflict',
45
+ throw new Exception('The HQ runtime owner is unverifiable', {
46
+ code: 'JG_AGENT_HQ_RUNTIME_OWNER_UNVERIFIABLE',
47
+ data: {
48
+ kind: 'state-conflict',
49
+ },
43
50
  });
44
51
  const response = HqResponseSchema.safeParse(await handle(input.data.command));
45
52
  if (!response.success || response.data.kind !== input.data.command.kind)
46
- throw new Exception('The HQ transport failed', undefined, { kind: 'operation-failed' });
53
+ throw new Exception('The HQ transport failed', {
54
+ code: 'JG_AGENT_HQ_TRANSPORT_FAILED',
55
+ data: { kind: 'operation-failed' },
56
+ });
47
57
  send({ schemaVersion: 1, id, success: true, response: response.data });
48
58
  }
49
59
  catch (error) {
@@ -90,7 +100,11 @@ export const createIpcServer = async (path, runtimeEpoch, handle) => {
90
100
  socket.destroy();
91
101
  if (server.listening)
92
102
  await new Promise((resolve) => server.close(() => resolve()));
93
- throw new Exception(`The HQ transport failed: ${extractMessage(cause)}`, undefined, { kind: 'operation-failed' }, { cause });
103
+ throw new Exception(`The HQ transport failed: ${extractMessage(cause)}`, {
104
+ code: 'JG_AGENT_HQ_TRANSPORT_FAILED',
105
+ data: { kind: 'operation-failed' },
106
+ cause,
107
+ });
94
108
  }
95
109
  let closing;
96
110
  return {
@@ -101,7 +115,11 @@ export const createIpcServer = async (path, runtimeEpoch, handle) => {
101
115
  for (const socket of sockets)
102
116
  socket.destroy();
103
117
  server.close((error) => error
104
- ? reject(new Exception(`The HQ transport failed: ${extractMessage(error)}`, undefined, { kind: 'operation-failed' }, { cause: error }))
118
+ ? reject(new Exception(`The HQ transport failed: ${extractMessage(error)}`, {
119
+ code: 'JG_AGENT_HQ_TRANSPORT_FAILED',
120
+ data: { kind: 'operation-failed' },
121
+ cause: error,
122
+ }))
105
123
  : resolve());
106
124
  })),
107
125
  };