jg-agent-hq 0.3.0 → 0.5.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 +11 -0
- package/README.md +3 -1
- package/api/README.md +27 -11
- package/dist/actions/live/live.d.ts +4 -1
- package/dist/actions/live/live.js +22 -6
- package/dist/actions/reads.d.ts +1 -1
- package/dist/actions/reads.js +4 -2
- package/dist/contracts/browser/index.d.ts +5 -0
- package/dist/contracts/browser/index.js +6 -0
- package/dist/contracts/browser/types.d.ts +2547 -0
- package/dist/contracts/browser/types.js +150 -0
- package/dist/contracts/index.d.ts +5 -0
- package/dist/contracts/index.js +6 -0
- package/dist/contracts/types.d.ts +66 -0
- package/dist/contracts/types.js +10 -2
- package/dist/providers/claude/history/types.d.ts +28 -0
- package/dist/runtime/browser/browser.d.ts +8 -0
- package/dist/runtime/browser/browser.js +55 -0
- package/dist/runtime/browser/index.d.ts +1 -0
- package/dist/runtime/browser/index.js +2 -0
- package/dist/runtime/runtime.js +12 -1
- package/dist/runtime/types.d.ts +19 -1
- package/dist/store/browser/browser.model.d.ts +24 -0
- package/dist/store/browser/browser.model.js +75 -0
- package/dist/store/browser/index.d.ts +1 -0
- package/dist/store/browser/index.js +2 -0
- package/dist/store/schema.sql +10 -0
- package/dist/store/store.js +4 -1
- package/dist/store/store.worker.js +11 -0
- package/dist/store/types.d.ts +1504 -467
- package/dist/store/types.js +17 -1
- package/errors/README.md +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
- Add safe publication observations for the host's System view, with separate pending, blocked, failed and uncertain states, timestamps and exact commit identity.
|
|
6
|
+
- Bind publication retries to a receipt UUID and nullable expected commit, including failures that happened before a commit existed. The host continues to own scheduling, Git operations and storage.
|
|
7
|
+
|
|
8
|
+
## 0.4.0
|
|
9
|
+
|
|
10
|
+
- Add closed browser host commands and bounded session/repository/plan read contracts, without exposing private handlers through standalone IPC.
|
|
11
|
+
- Persist operator-bound host action receipts before effects, reject changed replays, retain uncertainty across restart and join exact server-owned native child receipts.
|
|
12
|
+
- Add conditional live-window reads so unchanged SSE polling does not repeatedly copy or serialize conversation bodies.
|
|
13
|
+
|
|
3
14
|
## 0.3.0
|
|
4
15
|
|
|
5
16
|
- Publish the consumer-tested package through the configured GitHub Actions trusted publisher before deploying the website. Verify release ordering and archive integrity, and safely skip identical published versions.
|
package/README.md
CHANGED
|
@@ -45,7 +45,9 @@ Use `--provider claude` for Claude history and usage; the default is `codex`. Cl
|
|
|
45
45
|
|
|
46
46
|
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
47
|
|
|
48
|
-
Native execution settings, instructions, skills and tool permissions remain with each CLI. Claude uses an owned structured process per controlled session and reports immediate steering as unavailable. Plan approval grants no native tool permission. Close other CLI controllers before confirming adoption, resume or recovery. HQ coordinates sessions sharing one state directory; it cannot lock independently running native CLIs.
|
|
48
|
+
Native execution settings, instructions, skills and tool permissions remain with each CLI. Claude uses an owned structured process per controlled session and reports immediate steering as unavailable. Plan approval grants no native tool permission. Close other CLI controllers before confirming adoption, resume or recovery. HQ coordinates sessions sharing one state directory; it cannot lock independently running native CLIs. Integrating browser hosts can use durable host-action admission and operator-scoped reconciliation through the programmatic API; see the [browser host contract](api/README.md#browser-host-integration). The standalone CLI advertises only commands it implements.
|
|
49
|
+
|
|
50
|
+
Durable state includes `action.key`, which must stay private and accompany its SQLite database. Queued undelivered text is saved; ordinary message bodies and question answers are not.
|
|
49
51
|
|
|
50
52
|
Use `suggest-thread-name --topic "Build usage" --state-root <directory>` for an editable label and `managed-thread-name --name build-usage --state-root <directory>` to resolve it. Creation adds a short ordinal suffix when a name is occupied. Actions and relationships retain immutable IDs.
|
|
51
53
|
|
package/api/README.md
CHANGED
|
@@ -124,17 +124,17 @@ Trusted runtime construction also accepts `codexExtraSkillRoots?: string[]` and
|
|
|
124
124
|
|
|
125
125
|
## Control reads and reconnect
|
|
126
126
|
|
|
127
|
-
| Command | Fields | Result
|
|
128
|
-
| --------------------- | ------------------------------------------------- |
|
|
129
|
-
| `managed-threads` | `workspaceRoot`, optional `limit`, `cursor` | Creation/UUID seek page of HQ records, including inactive and uncertain states.
|
|
130
|
-
| `managed-thread` | `threadId` | One HQ record or null.
|
|
131
|
-
| `managed-thread-name` | `name` | One HQ record or null, with case-insensitive lookup and the same workspace authorization.
|
|
132
|
-
| `suggest-thread-name` | `topic` | An editable lowercase label, with a currently free suffix when needed. This read does not reserve it.
|
|
133
|
-
| `receipt` | `actionId` | One receipt or null.
|
|
134
|
-
| `actions` | `threadId`, optional `limit`, `cursor` | Creation/UUID seek page of action metadata without queued bodies.
|
|
135
|
-
| `events` | `threadId`, optional `after` (default 0), `limit` | Ascending durable sequence page and `hasMore`; continue after the final sequence.
|
|
136
|
-
| `approvals` | `threadId` | Current in-memory native prompts, including exact scope and question metadata.
|
|
137
|
-
| `live-items` | `threadId` | Active/awaiting-flush item window and `
|
|
127
|
+
| Command | Fields | Result |
|
|
128
|
+
| --------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
129
|
+
| `managed-threads` | `workspaceRoot`, optional `limit`, `cursor` | Creation/UUID seek page of HQ records, including inactive and uncertain states. |
|
|
130
|
+
| `managed-thread` | `threadId` | One HQ record or null. |
|
|
131
|
+
| `managed-thread-name` | `name` | One HQ record or null, with case-insensitive lookup and the same workspace authorization. |
|
|
132
|
+
| `suggest-thread-name` | `topic` | An editable lowercase label, with a currently free suffix when needed. This read does not reserve it. |
|
|
133
|
+
| `receipt` | `actionId` | One receipt or null. |
|
|
134
|
+
| `actions` | `threadId`, optional `limit`, `cursor` | Creation/UUID seek page of action metadata without queued bodies. |
|
|
135
|
+
| `events` | `threadId`, optional `after` (default 0), `limit` | Ascending durable sequence page and `hasMore`; continue after the final sequence. |
|
|
136
|
+
| `approvals` | `threadId` | Current in-memory native prompts, including exact scope and question metadata. |
|
|
137
|
+
| `live-items` | `threadId` | Active/awaiting-flush item window, `hasGap`, `revision` and `unchanged`. Optional `afterRevision` omits bodies for an unchanged window. |
|
|
138
138
|
|
|
139
139
|
The installed CLI exposes `managed-thread-name --name <name>` and `suggest-thread-name --topic <topic>`. Suggestions normalize punctuation to hyphens, keep at most 32 characters, and use `session` when the topic contains no ASCII letters or digits. Creation checks availability again inside its admission transaction. All action targets, histories and relationships continue to use immutable HQ/native identities. Name lookup never adopts, resumes or starts a native session.
|
|
140
140
|
|
|
@@ -255,3 +255,19 @@ Feedback resolves installed `jg-feedback` in the author thread and carries the s
|
|
|
255
255
|
Delivery is `prepared`, `queued`, `submitted`, `delivered`, `uncertain`, `failed` or `canceled`; native `completion` is separately null, `completed`, `failed` or `interrupted`. Acknowledgement alone becomes `delivered`. Busy destinations use native next-turn queues, including while waiting for permissions; feedback is never written as a permission answer. Pending native permissions remain explicit actions on their own channels. No automatic feedback, response loop, approval or publication follows collection.
|
|
256
256
|
|
|
257
257
|
Lists use indexed `createdAt DESC, id DESC` seek ordering, default 32/max 128, and cursors bound to the workspace, source filter and page size. Concurrent newer inserts do not reappear on continuation; deleted unseen records disappear. Summaries do not transfer retained finding bodies through the worker. SQLite owns the small private `handoff` and `handoff_dispatch` records alongside existing actions. No database migration or transcript mirror is required. Reads are fresh and bounded; no separate cache is used.
|
|
258
|
+
|
|
259
|
+
## Browser host integration
|
|
260
|
+
|
|
261
|
+
`jg-agent-hq/contracts` exports closed `BrowserActionSchema`, `BrowserCommandSchema` and `BrowserReceiptSchema` contracts, plus browser session, repository and bounded plan read models. These schemas contain generic identities, not a deployment registry or private handler implementation. They do not expose native RPC, executables or provider configuration. Standalone IPC continues to support only its existing owned commands.
|
|
262
|
+
|
|
263
|
+
A trusted Node host calls `runtime.performBrowserAction(request, operatorId, execute)` after validating its signed operator identity. The host supplies one dispatcher function in process. It must validate every target against its registry, check operation-specific revisions/turns, and normalize approval provenance to that authenticated operator. Never accept a callback, operator identity or handler name from HTTP input. The request has a stable UUID, current runtime epoch and one closed command: native, plan, handoff, priority, error-reservation or publication-retry. The complete serialized request is limited to 2 MiB; nested native commands retain their tighter limits.
|
|
264
|
+
|
|
265
|
+
SQLite records an HMAC digest and receipt before invoking the dispatcher. The same UUID and identical normalized request returns the stored receipt without invoking it again. Changed payloads or operators conflict. New requests require the current epoch. Replaying a saved request after restart reconciles it without granting execution authority. Submitted receipts from another runtime become uncertain. Dispatcher failure also leaves the receipt uncertain because its effects may already have committed. This state never automatically resends an effect.
|
|
266
|
+
|
|
267
|
+
The dispatcher receives a server-owned `nativeActionId`; use this ID for a native child action, preserving the validated runtime and command. `readBrowserAction(id, operatorId)` returns only that operator's receipt and joins its exact native receipt. Host acknowledgement means the handler completed, not that a native turn completed. An unknown or differently owned ID returns null. Receipts retain metadata rather than message bodies or arbitrary handler output. Subsequent domain reads provide the result. At most 32 host jobs are admitted concurrently. Runtime close rejects new host work and waits for admitted work before closing its worker.
|
|
268
|
+
|
|
269
|
+
`PublicationStatusSchema` is a browser-safe observation supplied by the host, independent of native execution and quota states. It contains the receipt UUID, state/reason, due/attempt/success timestamps, nullable pending and published commit hashes, and bounded queued/selected file counts. It contains no artifact bodies, paths, credentials or deployment configuration. `publication-retry` requires `publicationId` and `expectedCommit` (null before a commit exists). The host must compare both against its current receipt before acting and reconcile or retry only its recorded verified commit. The public runtime supplies no Git publisher, service installer or publication storage.
|
|
270
|
+
|
|
271
|
+
Live reads accept `afterRevision`. A matching revision returns `unchanged: true` with an empty data array; retain the displayed window. A changed revision returns the complete bounded window. Revisions belong to one runtime. After reconnect to a new runtime, discard the revision and refresh native history. Token bodies stay in the existing bounded memory window and are never copied into lifecycle or browser-action tables.
|
|
272
|
+
|
|
273
|
+
`thread-plan` accepts a managed `threadId` and returns its nullable `ActionPlanContextSchema`. The host verifies the thread's registered checkout before exposing it. It is read-only and grants no execution authority.
|
|
@@ -3,6 +3,7 @@ import type { IHistoryItem } from '../../contracts/index.js';
|
|
|
3
3
|
export declare class LiveItems {
|
|
4
4
|
private readonly __threads;
|
|
5
5
|
private __bytes;
|
|
6
|
+
private __revision;
|
|
6
7
|
/** Starts a known presentation window without discarding retained awaiting-flush items. */
|
|
7
8
|
open(nativeId: string): void;
|
|
8
9
|
/** Replaces one native item without persisting token or tool-output bodies. */
|
|
@@ -14,7 +15,9 @@ export declare class LiveItems {
|
|
|
14
15
|
/** Appends bounded text; a truncated prefix is never presented as the complete item. */
|
|
15
16
|
append(nativeId: string, turnId: string, itemId: string, delta: string): void;
|
|
16
17
|
/** Returns a copy of the bounded window and explicit incompleteness evidence. */
|
|
17
|
-
read(nativeId: string): {
|
|
18
|
+
read(nativeId: string, afterRevision?: number): {
|
|
19
|
+
revision: number;
|
|
20
|
+
unchanged: boolean;
|
|
18
21
|
data: (IHistoryItem & {
|
|
19
22
|
state: 'active' | 'awaiting-flush';
|
|
20
23
|
})[];
|
|
@@ -4,13 +4,14 @@ const MAX_LIVE_BYTES = 4 * 1024 * 1024;
|
|
|
4
4
|
export class LiveItems {
|
|
5
5
|
__threads = new Map();
|
|
6
6
|
__bytes = 0;
|
|
7
|
+
__revision = 0;
|
|
7
8
|
/** Starts a known presentation window without discarding retained awaiting-flush items. */
|
|
8
9
|
open(nativeId) {
|
|
9
10
|
if (this.__threads.has(nativeId))
|
|
10
11
|
return;
|
|
11
12
|
if (this.__threads.size >= 32)
|
|
12
13
|
this.__remove(this.__threads.keys().next().value);
|
|
13
|
-
this.__threads.set(nativeId, { items: new Map(), hasGap: false });
|
|
14
|
+
this.__threads.set(nativeId, { items: new Map(), hasGap: false, revision: ++this.__revision });
|
|
14
15
|
}
|
|
15
16
|
/** Replaces one native item without persisting token or tool-output bodies. */
|
|
16
17
|
put(nativeId, item, isComplete) {
|
|
@@ -18,9 +19,10 @@ export class LiveItems {
|
|
|
18
19
|
if (!thread) {
|
|
19
20
|
if (this.__threads.size >= 32)
|
|
20
21
|
this.__remove(this.__threads.keys().next().value);
|
|
21
|
-
thread = { items: new Map(), hasGap: true };
|
|
22
|
+
thread = { items: new Map(), hasGap: true, revision: ++this.__revision };
|
|
22
23
|
this.__threads.set(nativeId, thread);
|
|
23
24
|
}
|
|
25
|
+
thread.revision = ++this.__revision;
|
|
24
26
|
const key = JSON.stringify([item.turnId, item.nativeId]);
|
|
25
27
|
const previous = thread.items.get(key);
|
|
26
28
|
if (previous)
|
|
@@ -40,8 +42,10 @@ export class LiveItems {
|
|
|
40
42
|
/** Marks missing native stream evidence without discarding the surviving presentation. */
|
|
41
43
|
markGap(nativeId) {
|
|
42
44
|
const thread = this.__threads.get(nativeId);
|
|
43
|
-
if (thread)
|
|
45
|
+
if (thread) {
|
|
44
46
|
thread.hasGap = true;
|
|
47
|
+
thread.revision = ++this.__revision;
|
|
48
|
+
}
|
|
45
49
|
}
|
|
46
50
|
/** Replaces an ephemeral stream identity when the native persisted identity arrives. */
|
|
47
51
|
replace(nativeId, turnId, itemId) {
|
|
@@ -52,14 +56,17 @@ export class LiveItems {
|
|
|
52
56
|
return;
|
|
53
57
|
this.__bytes -= entry.bytes;
|
|
54
58
|
thread.items.delete(key);
|
|
59
|
+
thread.revision = ++this.__revision;
|
|
55
60
|
}
|
|
56
61
|
/** Appends bounded text; a truncated prefix is never presented as the complete item. */
|
|
57
62
|
append(nativeId, turnId, itemId, delta) {
|
|
58
63
|
const thread = this.__threads.get(nativeId);
|
|
59
64
|
const entry = thread?.items.get(JSON.stringify([turnId, itemId]));
|
|
60
65
|
if (!entry || entry.isComplete) {
|
|
61
|
-
if (thread)
|
|
66
|
+
if (thread) {
|
|
62
67
|
thread.hasGap = true;
|
|
68
|
+
thread.revision = ++this.__revision;
|
|
69
|
+
}
|
|
63
70
|
return;
|
|
64
71
|
}
|
|
65
72
|
if (entry.item.isTruncated)
|
|
@@ -75,10 +82,13 @@ export class LiveItems {
|
|
|
75
82
|
}, false);
|
|
76
83
|
}
|
|
77
84
|
/** Returns a copy of the bounded window and explicit incompleteness evidence. */
|
|
78
|
-
read(nativeId) {
|
|
85
|
+
read(nativeId, afterRevision) {
|
|
79
86
|
const thread = this.__threads.get(nativeId);
|
|
87
|
+
const revision = thread?.revision ?? this.__revision;
|
|
80
88
|
return {
|
|
81
|
-
|
|
89
|
+
revision,
|
|
90
|
+
unchanged: afterRevision === revision,
|
|
91
|
+
data: thread && afterRevision !== revision
|
|
82
92
|
? [...thread.items.values()].map((entry) => ({
|
|
83
93
|
...entry.item,
|
|
84
94
|
state: entry.isComplete ? 'awaiting-flush' : 'active',
|
|
@@ -98,12 +108,15 @@ export class LiveItems {
|
|
|
98
108
|
if (entry?.isComplete && JSON.stringify(entry.item) === JSON.stringify(item)) {
|
|
99
109
|
this.__bytes -= entry.bytes;
|
|
100
110
|
thread.items.delete(key);
|
|
111
|
+
thread.revision = ++this.__revision;
|
|
101
112
|
}
|
|
102
113
|
}
|
|
103
114
|
}
|
|
104
115
|
/** Keeps interrupted partial items available while their final native history is pending. */
|
|
105
116
|
completeTurn(nativeId, turnId) {
|
|
106
117
|
const thread = this.__threads.get(nativeId);
|
|
118
|
+
if (thread)
|
|
119
|
+
thread.revision = ++this.__revision;
|
|
107
120
|
if (thread)
|
|
108
121
|
for (const entry of thread.items.values())
|
|
109
122
|
if (entry.item.turnId === turnId)
|
|
@@ -116,10 +129,12 @@ export class LiveItems {
|
|
|
116
129
|
for (const entry of thread.items.values())
|
|
117
130
|
this.__bytes -= entry.bytes;
|
|
118
131
|
this.__threads.delete(nativeId);
|
|
132
|
+
this.__revision += 1;
|
|
119
133
|
}
|
|
120
134
|
/** Releases every retained body when the owning runtime has stopped. */
|
|
121
135
|
clear() {
|
|
122
136
|
this.__threads.clear();
|
|
137
|
+
this.__revision += 1;
|
|
123
138
|
this.__bytes = 0;
|
|
124
139
|
}
|
|
125
140
|
__drop(thread, key) {
|
|
@@ -128,6 +143,7 @@ export class LiveItems {
|
|
|
128
143
|
return;
|
|
129
144
|
this.__bytes -= entry.bytes;
|
|
130
145
|
thread.items.delete(key);
|
|
146
|
+
thread.revision = ++this.__revision;
|
|
131
147
|
thread.hasGap = true;
|
|
132
148
|
}
|
|
133
149
|
}
|
package/dist/actions/reads.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { IHqRequest, IHqResponse } from '../contracts/index.js';
|
|
|
2
2
|
import type { IStore } from '../store/index.js';
|
|
3
3
|
import type { createActions } from './actions.js';
|
|
4
4
|
type IControlRead = Extract<IHqRequest, {
|
|
5
|
-
kind: 'receipt' | 'managed-threads' | 'managed-thread' | 'managed-thread-name' | 'actions' | 'events' | 'approvals' | 'live-items';
|
|
5
|
+
kind: 'thread-plan' | 'receipt' | 'managed-threads' | 'managed-thread' | 'managed-thread-name' | 'actions' | 'events' | 'approvals' | 'live-items';
|
|
6
6
|
}>;
|
|
7
7
|
/**
|
|
8
8
|
* Authorizes bounded control reads and binds seek positions to their exact query.
|
package/dist/actions/reads.js
CHANGED
|
@@ -75,6 +75,8 @@ export const readControl = async (command, store, actions, roots) => {
|
|
|
75
75
|
}
|
|
76
76
|
const thread = await store.getThread(command.threadId);
|
|
77
77
|
await authorize(thread);
|
|
78
|
+
if (command.kind === 'thread-plan')
|
|
79
|
+
return { kind: 'thread-plan', result: thread ? await store.getThreadPlan(thread.id) : null };
|
|
78
80
|
if (command.kind === 'managed-thread')
|
|
79
81
|
return { kind: command.kind, result: thread };
|
|
80
82
|
if (!thread)
|
|
@@ -95,7 +97,7 @@ export const readControl = async (command, store, actions, roots) => {
|
|
|
95
97
|
return {
|
|
96
98
|
kind: 'live-items',
|
|
97
99
|
result: thread.nativeId
|
|
98
|
-
? actions.live.read(JSON.stringify([thread.provider, thread.nativeId]))
|
|
99
|
-
: { data: [], hasGap: false },
|
|
100
|
+
? actions.live.read(JSON.stringify([thread.provider, thread.nativeId]), command.afterRevision)
|
|
101
|
+
: { data: [], hasGap: false, revision: 0, unchanged: command.afterRevision === 0 },
|
|
100
102
|
};
|
|
101
103
|
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { BrowserCommandSchema, BrowserActionSchema, BrowserReceiptSchema } from './types.js';
|
|
2
|
+
export type { IBrowserAction, IBrowserReceipt } from './types.js';
|
|
3
|
+
export { BrowserRepositorySchema, BrowserPlanSummarySchema, BrowserPlanPageSchema, BrowserPlanDetailSchema, BrowserSessionSchema, } from './types.js';
|
|
4
|
+
export { BrowserRepositoryPageSchema, BrowserSystemSchema, BrowserThreadDetailSchema, PublicationStatusSchema, } from './types.js';
|
|
5
|
+
export type { IPublicationStatus } from './types.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// browser requests and durable reconciliation
|
|
2
|
+
export { BrowserCommandSchema, BrowserActionSchema, BrowserReceiptSchema } from './types.js';
|
|
3
|
+
// host read models
|
|
4
|
+
export { BrowserRepositorySchema, BrowserPlanSummarySchema, BrowserPlanPageSchema, BrowserPlanDetailSchema, BrowserSessionSchema, } from './types.js';
|
|
5
|
+
// bounded host views
|
|
6
|
+
export { BrowserRepositoryPageSchema, BrowserSystemSchema, BrowserThreadDetailSchema, PublicationStatusSchema, } from './types.js';
|