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.
- package/CHANGELOG.md +12 -0
- package/README.md +5 -1
- package/api/README.md +8 -4
- package/dist/actions/actions.d.ts +17 -17
- package/dist/actions/actions.js +56 -37
- package/dist/actions/reads.d.ts +4 -4
- package/dist/actions/reads.js +16 -9
- package/dist/cli/cli.js +80 -21
- package/dist/contracts/constants.d.ts +9 -0
- package/dist/contracts/constants.js +10 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/index.js +1 -1
- package/dist/errors/utilities.js +2 -2
- package/dist/handoffs/handoffs.d.ts +25 -25
- package/dist/handoffs/handoffs.js +93 -77
- package/dist/ipc/client.d.ts +13 -13
- package/dist/ipc/client.js +53 -25
- package/dist/ipc/framing.d.ts +1 -1
- package/dist/ipc/framing.js +15 -5
- package/dist/ipc/server.d.ts +1 -1
- package/dist/ipc/server.js +26 -8
- package/dist/locks/locks.d.ts +38 -38
- package/dist/locks/locks.js +99 -69
- package/dist/plans/admission.d.ts +36 -36
- package/dist/plans/admission.js +42 -39
- package/dist/plans/authority.d.ts +1 -1
- package/dist/plans/authority.js +7 -4
- package/dist/plans/evidence.d.ts +4 -4
- package/dist/plans/evidence.js +64 -29
- package/dist/plans/files.d.ts +8 -8
- package/dist/plans/files.js +59 -21
- package/dist/plans/finalization.d.ts +2 -2
- package/dist/plans/finalization.js +8 -5
- package/dist/plans/plans.d.ts +76 -76
- package/dist/plans/plans.js +91 -85
- package/dist/plans/projections.d.ts +2 -2
- package/dist/plans/projections.js +18 -7
- package/dist/plans/reconciliation.d.ts +3 -3
- package/dist/plans/reconciliation.js +13 -7
- package/dist/plans/revisions.d.ts +7 -7
- package/dist/plans/revisions.js +13 -10
- package/dist/plans/utilities.d.ts +2 -2
- package/dist/plans/utilities.js +36 -11
- package/dist/providers/claude/claude.d.ts +6 -6
- package/dist/providers/claude/claude.js +29 -16
- package/dist/providers/claude/control/control.d.ts +19 -19
- package/dist/providers/claude/control/control.js +45 -33
- package/dist/providers/claude/control/permissions.js +5 -2
- package/dist/providers/claude/control/skills.d.ts +1 -1
- package/dist/providers/claude/control/skills.js +12 -5
- package/dist/providers/claude/control/stream.d.ts +1 -1
- package/dist/providers/claude/control/stream.js +6 -3
- package/dist/providers/claude/history/history.model.d.ts +2 -2
- package/dist/providers/claude/history/history.model.js +41 -22
- package/dist/providers/claude/history/utilities.d.ts +8 -3
- package/dist/providers/claude/history/utilities.js +68 -17
- package/dist/providers/claude/rpc.d.ts +3 -3
- package/dist/providers/claude/rpc.js +56 -19
- package/dist/providers/claude/transformers.d.ts +1 -1
- package/dist/providers/claude/transformers.js +7 -4
- package/dist/providers/claude/types.d.ts +7 -1
- package/dist/providers/claude/types.js +6 -1
- package/dist/providers/codex/approvals/utilities.d.ts +1 -1
- package/dist/providers/codex/approvals/utilities.js +6 -3
- package/dist/providers/codex/codex.d.ts +2 -2
- package/dist/providers/codex/codex.js +48 -21
- package/dist/providers/codex/rpc.d.ts +2 -2
- package/dist/providers/codex/rpc.js +60 -19
- package/dist/providers/codex/transformers.d.ts +4 -4
- package/dist/providers/codex/transformers.js +4 -4
- package/dist/providers/codex/types.d.ts +12 -12
- package/dist/providers/codex/validations.d.ts +1 -1
- package/dist/providers/codex/validations.js +7 -3
- package/dist/providers/process.d.ts +1 -1
- package/dist/providers/process.js +16 -7
- package/dist/providers/types.d.ts +3 -3
- package/dist/runtime/browser/browser.js +14 -5
- package/dist/runtime/runtime.d.ts +13 -13
- package/dist/runtime/runtime.js +49 -24
- package/dist/runtime/state/state.d.ts +18 -18
- package/dist/runtime/state/state.js +100 -45
- package/dist/runtime/types.d.ts +37 -37
- package/dist/sessions/sessions.d.ts +4 -4
- package/dist/sessions/sessions.js +15 -9
- package/dist/sessions/utilities.d.ts +1 -1
- package/dist/sessions/utilities.js +16 -6
- package/dist/skills/skills.d.ts +10 -10
- package/dist/skills/skills.js +27 -17
- package/dist/store/browser/browser.model.d.ts +1 -1
- package/dist/store/browser/browser.model.js +7 -4
- package/dist/store/control/control.model.d.ts +4 -4
- package/dist/store/control/control.model.js +10 -7
- package/dist/store/handoffs/handoffs.model.d.ts +3 -3
- package/dist/store/handoffs/handoffs.model.js +9 -6
- package/dist/store/plans/plans.model.d.ts +3 -3
- package/dist/store/plans/plans.model.js +29 -14
- package/dist/store/store.d.ts +1 -1
- package/dist/store/store.js +42 -13
- package/dist/store/store.worker.js +8 -4
- package/dist/store/types.d.ts +52 -51
- package/dist/store/types.js +3 -1
- package/errors/README.md +62 -62
- 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
|
|
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
|
|
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
|
-
* -
|
|
18
|
-
* -
|
|
19
|
-
* -
|
|
20
|
-
* -
|
|
21
|
-
* -
|
|
22
|
-
* -
|
|
23
|
-
* -
|
|
24
|
-
* -
|
|
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
|
-
* -
|
|
34
|
-
* -
|
|
35
|
-
* -
|
|
36
|
-
* -
|
|
37
|
-
* -
|
|
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
|
-
* -
|
|
45
|
-
* -
|
|
46
|
-
* -
|
|
47
|
-
* -
|
|
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
|
};
|
package/dist/actions/actions.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHmac, randomUUID } from 'node:crypto';
|
|
2
|
-
import { Exception, extractMessage } from '
|
|
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',
|
|
44
|
-
|
|
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',
|
|
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',
|
|
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
|
-
* -
|
|
183
|
-
* -
|
|
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
|
|
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
|
-
* -
|
|
624
|
-
* -
|
|
625
|
-
* -
|
|
626
|
-
* -
|
|
627
|
-
* -
|
|
628
|
-
* -
|
|
629
|
-
* -
|
|
630
|
-
* -
|
|
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
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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
|
-
* -
|
|
808
|
-
* -
|
|
809
|
-
* -
|
|
810
|
-
* -
|
|
811
|
-
* -
|
|
812
|
-
* -
|
|
813
|
-
* -
|
|
814
|
-
* -
|
|
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
|
-
* -
|
|
837
|
-
* -
|
|
838
|
-
* -
|
|
839
|
-
* -
|
|
840
|
-
* -
|
|
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
|
-
* -
|
|
858
|
-
* -
|
|
859
|
-
* -
|
|
860
|
-
* -
|
|
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)}`,
|
|
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;
|
package/dist/actions/reads.d.ts
CHANGED
|
@@ -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
|
-
* -
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
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 {};
|
package/dist/actions/reads.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Exception } from '
|
|
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
|
-
* -
|
|
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',
|
|
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
|
-
* -
|
|
32
|
-
* -
|
|
33
|
-
* -
|
|
34
|
-
* -
|
|
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',
|
|
84
|
-
|
|
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 '
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
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',
|
|
207
|
-
|
|
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
|
|
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',
|
|
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.',
|