jg-agent-hq 2.0.0 → 3.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 +9 -0
- package/README.md +4 -4
- package/api/README.md +51 -8
- package/dist/cli/cli.js +35 -3
- package/dist/cli/utilities.d.ts +3 -0
- package/dist/cli/utilities.js +30 -0
- package/dist/contracts/index.d.ts +2 -2
- package/dist/contracts/index.js +1 -1
- package/dist/contracts/types.d.ts +102 -0
- package/dist/contracts/types.js +24 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/plans/authority.d.ts +2 -0
- package/dist/plans/authority.js +8 -0
- package/dist/plans/documents.d.ts +17 -0
- package/dist/plans/documents.js +24 -2
- package/dist/plans/files.js +3 -1
- package/dist/plans/plans.js +3 -1
- package/dist/plans/types.d.ts +5 -0
- package/dist/providers/claude/claude.js +2 -0
- package/dist/providers/claude/history/types.d.ts +23 -0
- package/dist/providers/claude/transformers.js +14 -1
- package/dist/providers/codex/codex.js +4 -0
- package/dist/providers/codex/transformers.js +30 -3
- package/dist/providers/codex/types.d.ts +34 -1
- package/dist/providers/codex/types.js +11 -2
- package/dist/runtime/runtime.d.ts +2 -0
- package/dist/runtime/runtime.js +2 -0
- package/dist/runtime/state/index.d.ts +1 -1
- package/dist/runtime/state/index.js +1 -1
- package/dist/runtime/state/state.d.ts +16 -1
- package/dist/runtime/state/state.js +94 -3
- package/dist/snapshots/capture.d.ts +14 -0
- package/dist/snapshots/capture.js +142 -0
- package/dist/snapshots/database.d.ts +2 -0
- package/dist/snapshots/database.js +15 -51
- package/dist/snapshots/fences.d.ts +15 -0
- package/dist/snapshots/fences.js +64 -0
- package/dist/snapshots/index.d.ts +2 -0
- package/dist/snapshots/index.js +3 -0
- package/dist/snapshots/snapshots.d.ts +4 -1
- package/dist/snapshots/snapshots.js +27 -113
- package/dist/snapshots/snapshots.worker.js +5 -1
- package/dist/snapshots/types.d.ts +11 -0
- package/dist/snapshots/types.js +5 -1
- package/dist/storage/index.d.ts +2 -1
- package/dist/storage/index.js +2 -1
- package/dist/storage/schema/constants.d.ts +2 -0
- package/dist/storage/schema/constants.js +3 -0
- package/dist/storage/schema/index.d.ts +4 -0
- package/dist/storage/schema/index.js +5 -0
- package/dist/storage/schema/schema.d.ts +16 -0
- package/dist/storage/schema/schema.js +73 -0
- package/dist/storage/schema/types.d.ts +17 -0
- package/dist/storage/schema/types.js +8 -0
- package/dist/storage/schema/v1-to-v2.sql +1 -0
- package/dist/storage/schema/v1.sql +538 -0
- package/dist/storage/types.d.ts +0 -5
- package/dist/storage/types.js +0 -6
- package/dist/storage/upgrade/index.d.ts +2 -0
- package/dist/storage/upgrade/index.js +2 -0
- package/dist/storage/upgrade/types.d.ts +61 -0
- package/dist/storage/upgrade/types.js +42 -0
- package/dist/storage/upgrade/upgrade.d.ts +33 -0
- package/dist/storage/upgrade/upgrade.js +196 -0
- package/dist/storage/upgrade/upgrade.worker.d.ts +1 -0
- package/dist/storage/upgrade/upgrade.worker.js +220 -0
- package/dist/store/schema.d.ts +2 -0
- package/dist/store/schema.js +16 -25
- package/dist/store/schema.sql +2 -1
- package/dist/store/store.d.ts +2 -0
- package/dist/store/store.js +2 -0
- package/docs/recovery.md +15 -1
- package/errors/README.md +34 -28
- package/package.json +2 -2
package/dist/plans/documents.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { hashValue as fingerprintSource } from 'jg-crypto';
|
|
2
2
|
import { Exception } from 'jg-exception';
|
|
3
|
-
import { fingerprintPlan, parseMilestones, parsePlan, preparePlanDocumentWrite, preparePlanUpdate, } from 'jg-plans';
|
|
3
|
+
import { checkPlan, fingerprintPlan, parseMilestones, parsePlan, preparePlanDocumentWrite, preparePlanUpdate, } from 'jg-plans';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { PlanMetadataSchema } from '../contracts/index.js';
|
|
6
6
|
const administrativeKeys = ['created_at', 'updated_at', 'revision', 'status'];
|
|
@@ -10,13 +10,35 @@ const bindingSchema = z.object({
|
|
|
10
10
|
plan_content_sha256: z.string().regex(/^[a-f0-9]{64}$/),
|
|
11
11
|
});
|
|
12
12
|
const metadataSchema = z.record(z.string(), z.json());
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Rejects a plan HQ cannot read or act on.
|
|
15
|
+
* @param cause The validation failure, when one exists.
|
|
16
|
+
* @throws
|
|
17
|
+
* - JG_AGENT_HQ_PLAN_INVALID: The HQ plan is invalid
|
|
18
|
+
*/
|
|
19
|
+
export const invalid = (cause) => {
|
|
14
20
|
throw new Exception('The HQ plan is invalid', {
|
|
15
21
|
code: 'JG_AGENT_HQ_PLAN_INVALID',
|
|
16
22
|
data: { kind: 'invalid-request' },
|
|
17
23
|
cause,
|
|
18
24
|
});
|
|
19
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* Checks a protocol 0.2 plan with jg-plans for the stages HQ gates. Plan approval needs plan.md
|
|
28
|
+
* to conform alone; finalization, sequence approval, authorization, and execution need it to
|
|
29
|
+
* conform with its milestones. Plans without the 0.2 marker always conform, as before.
|
|
30
|
+
* @param plan Exact plan.md source.
|
|
31
|
+
* @param milestones Exact milestones.md source, or null when absent.
|
|
32
|
+
* @returns The conformance of each stage.
|
|
33
|
+
*/
|
|
34
|
+
export const observePlanConformance = (plan, milestones) => {
|
|
35
|
+
if (parsePlan(plan).protocol !== '0.2')
|
|
36
|
+
return { plan: true, sequence: true };
|
|
37
|
+
return {
|
|
38
|
+
plan: checkPlan({ plan }).conforms,
|
|
39
|
+
sequence: checkPlan({ plan, ...(milestones === null ? {} : { milestones }) }).conforms,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
20
42
|
/** Metadata must be available as plain JSON; absence alone supplies an empty mapping. */
|
|
21
43
|
const metadataOf = (document) => {
|
|
22
44
|
if (!document.frontmatter)
|
package/dist/plans/files.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Exception, extractMessage, hasErrorCode, isException } from 'jg-excepti
|
|
|
6
6
|
import { PlanCoordinationSchema, } from '../contracts/index.js';
|
|
7
7
|
import { resolveWorkspaceTarget } from '../runtime/state/index.js';
|
|
8
8
|
import { computeObservedPlanRevision, hashPlanContent, projectPlanMilestones, } from './utilities.js';
|
|
9
|
-
import { observePlanContract, observeSkillsBinding, observeSequence } from './documents.js';
|
|
9
|
+
import { observePlanConformance, observePlanContract, observeSkillsBinding, observeSequence, } from './documents.js';
|
|
10
10
|
const MAX_PLAN_BYTES = 1_048_576;
|
|
11
11
|
/**
|
|
12
12
|
* Reads one bounded regular artifact without following a file symlink.
|
|
@@ -168,6 +168,8 @@ export const readPlanArtifacts = async (target, roots, parseContract) => {
|
|
|
168
168
|
milestones: milestones === null ? null : hashPlanContent(milestones),
|
|
169
169
|
coordination: coordinationText === null ? null : hashPlanContent(coordinationText),
|
|
170
170
|
},
|
|
171
|
+
// observed, not enforced: reads and revisions continue while stage gates use the result
|
|
172
|
+
conformance: observePlanConformance(plan, milestones),
|
|
171
173
|
};
|
|
172
174
|
};
|
|
173
175
|
/** Flushes fixed-name creation/removal before journal completion. */
|
package/dist/plans/plans.js
CHANGED
|
@@ -9,7 +9,7 @@ import { resolveWorkspaceTarget } from '../runtime/state/index.js';
|
|
|
9
9
|
import { canExecutePlanUnit, describePlan, updatePlanAuthority } from './authority.js';
|
|
10
10
|
import { createPlanAdmission } from './admission.js';
|
|
11
11
|
import { readPlanPage } from './projections.js';
|
|
12
|
-
import { prepareAggregateProgress, prepareSequenceMarkers } from './documents.js';
|
|
12
|
+
import { invalid, prepareAggregateProgress, prepareSequenceMarkers } from './documents.js';
|
|
13
13
|
import { finalizeInitialSequence } from './finalization.js';
|
|
14
14
|
import { beginPlanRevision, finishPlanCheckpoint, recordPlanCompletion, refreshPlanBaseline, } from './revisions.js';
|
|
15
15
|
import { capturePlanWorktrees } from './evidence.js';
|
|
@@ -594,6 +594,8 @@ export const createPlans = (coordinationRoot, roots, store, runtimeEpoch, parseC
|
|
|
594
594
|
}
|
|
595
595
|
}
|
|
596
596
|
else if (command.kind === 'finalize') {
|
|
597
|
+
if (!artifacts.conformance.sequence)
|
|
598
|
+
invalid();
|
|
597
599
|
if (artifacts.skillsBinding === 'stale')
|
|
598
600
|
conflict();
|
|
599
601
|
const workflow = coordination.revision;
|
package/dist/plans/types.d.ts
CHANGED
|
@@ -22,12 +22,17 @@ export type IPlanArtifacts = {
|
|
|
22
22
|
milestones: string | null;
|
|
23
23
|
coordination: string | null;
|
|
24
24
|
};
|
|
25
|
+
conformance: IPlanConformance;
|
|
25
26
|
};
|
|
26
27
|
export type IPlanContract = {
|
|
27
28
|
body: string;
|
|
28
29
|
metadata: IPlanMetadata;
|
|
29
30
|
milestones: string | null;
|
|
30
31
|
};
|
|
32
|
+
export type IPlanConformance = {
|
|
33
|
+
plan: boolean;
|
|
34
|
+
sequence: boolean;
|
|
35
|
+
};
|
|
31
36
|
export declare const PlanWriteIntentSchema: z.ZodObject<{
|
|
32
37
|
id: z.ZodUUID;
|
|
33
38
|
digest: z.ZodString;
|
|
@@ -4285,7 +4285,30 @@ export declare const ClaudeHistoryReadSchema: z.ZodUnion<readonly [z.ZodDiscrimi
|
|
|
4285
4285
|
remainingPercent: z.ZodNumber;
|
|
4286
4286
|
durationMinutes: z.ZodNullable<z.ZodNumber>;
|
|
4287
4287
|
resetsAt: z.ZodNullable<z.ZodNumber>;
|
|
4288
|
+
period: z.ZodOptional<z.ZodEnum<{
|
|
4289
|
+
"five-hour": "five-hour";
|
|
4290
|
+
"seven-day": "seven-day";
|
|
4291
|
+
other: "other";
|
|
4292
|
+
}>>;
|
|
4293
|
+
nativeKind: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
4294
|
+
group: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
4295
|
+
scope: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
4296
|
+
model: z.ZodNullable<z.ZodObject<{
|
|
4297
|
+
id: z.ZodNullable<z.ZodString>;
|
|
4298
|
+
displayName: z.ZodString;
|
|
4299
|
+
}, z.core.$strip>>;
|
|
4300
|
+
surface: z.ZodNullable<z.ZodString>;
|
|
4301
|
+
}, z.core.$strip>>>;
|
|
4288
4302
|
}, z.core.$strip>>;
|
|
4303
|
+
credits: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
4304
|
+
bucket: z.ZodString;
|
|
4305
|
+
hasCredits: z.ZodBoolean;
|
|
4306
|
+
isUnlimited: z.ZodBoolean;
|
|
4307
|
+
balance: z.ZodNullable<z.ZodString>;
|
|
4308
|
+
}, z.core.$strip>>>>;
|
|
4309
|
+
resetCredits: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
4310
|
+
availableCount: z.ZodNumber;
|
|
4311
|
+
}, z.core.$strip>>>;
|
|
4289
4312
|
}, z.core.$strip>;
|
|
4290
4313
|
}, z.core.$strip>, z.ZodObject<{
|
|
4291
4314
|
kind: z.ZodLiteral<"skills">;
|
|
@@ -27,8 +27,19 @@ export const toClaudeUsageSnapshot = (result, observedAt) => {
|
|
|
27
27
|
window: row.group === 'session' ? 'primary' : 'secondary',
|
|
28
28
|
usedPercent: row.percent,
|
|
29
29
|
remainingPercent: Math.max(0, 100 - row.percent),
|
|
30
|
-
durationMinutes: null,
|
|
30
|
+
durationMinutes: row.group === 'session' ? 300 : row.group === 'weekly' ? 10080 : null,
|
|
31
31
|
resetsAt: row.resets_at === null ? null : Date.parse(row.resets_at),
|
|
32
|
+
period: row.group === 'session' ? 'five-hour' : row.group === 'weekly' ? 'seven-day' : 'other',
|
|
33
|
+
nativeKind: row.kind,
|
|
34
|
+
group: row.group,
|
|
35
|
+
scope: row.scope
|
|
36
|
+
? {
|
|
37
|
+
model: row.scope.model
|
|
38
|
+
? { id: row.scope.model.id, displayName: row.scope.model.display_name }
|
|
39
|
+
: null,
|
|
40
|
+
surface: row.scope.surface,
|
|
41
|
+
}
|
|
42
|
+
: null,
|
|
32
43
|
});
|
|
33
44
|
}
|
|
34
45
|
return {
|
|
@@ -37,6 +48,8 @@ export const toClaudeUsageSnapshot = (result, observedAt) => {
|
|
|
37
48
|
observedAt,
|
|
38
49
|
ordinaryUsageAllowed: null,
|
|
39
50
|
windows,
|
|
51
|
+
credits: null,
|
|
52
|
+
resetCredits: null,
|
|
40
53
|
state: !result.rate_limits_available || windows.length === 0
|
|
41
54
|
? 'unavailable'
|
|
42
55
|
: result.rate_limits?.limits.some((row) => row.is_active && row.percent !== null && row.percent >= 100)
|
|
@@ -26,6 +26,8 @@ export const createCodexProvider = async (extraSkillRoots = [], agentTools) => {
|
|
|
26
26
|
observedAt: null,
|
|
27
27
|
ordinaryUsageAllowed: null,
|
|
28
28
|
windows: [],
|
|
29
|
+
credits: null,
|
|
30
|
+
resetCredits: null,
|
|
29
31
|
};
|
|
30
32
|
let rpc;
|
|
31
33
|
let refresh;
|
|
@@ -218,6 +220,8 @@ export const createCodexProvider = async (extraSkillRoots = [], agentTools) => {
|
|
|
218
220
|
observedAt: null,
|
|
219
221
|
ordinaryUsageAllowed: null,
|
|
220
222
|
windows: [],
|
|
223
|
+
credits: null,
|
|
224
|
+
resetCredits: null,
|
|
221
225
|
};
|
|
222
226
|
}
|
|
223
227
|
if (method === 'account/rateLimits/updated' &&
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { UsageSnapshotSchema, ThreadSummarySchema, TurnSummarySchema, HistoryItemSchema, } from '../../contracts/index.js';
|
|
2
|
+
import { UsageSnapshotSchema, UsageCreditsSchema, ThreadSummarySchema, TurnSummarySchema, HistoryItemSchema, } from '../../contracts/index.js';
|
|
3
3
|
import { parseNative } from './validations.js';
|
|
4
4
|
import { nativeProvenance } from '../../sessions/evidence/index.js';
|
|
5
5
|
/** Preserves exact native tree identities independently of managed-agent relationships. */
|
|
@@ -122,9 +122,25 @@ export const toHistoryItem = ({ turnId, item }, fullBody = false) => {
|
|
|
122
122
|
* - JG_AGENT_HQ_CODEX_FAILED: The Codex integration failed
|
|
123
123
|
*/
|
|
124
124
|
export const toUsageSnapshot = (usage, observedAt) => {
|
|
125
|
-
const
|
|
125
|
+
const nativeBuckets = usage.rateLimitsByLimitId;
|
|
126
|
+
const buckets = nativeBuckets && Object.keys(nativeBuckets).length > 0
|
|
127
|
+
? nativeBuckets
|
|
128
|
+
: usage.rateLimits
|
|
129
|
+
? { [usage.rateLimits.limitId ?? 'legacy']: usage.rateLimits }
|
|
130
|
+
: {};
|
|
126
131
|
const windows = [];
|
|
127
|
-
|
|
132
|
+
const credits = [];
|
|
133
|
+
for (const [bucketId, bucket] of Object.entries(buckets)) {
|
|
134
|
+
if (bucket.credits) {
|
|
135
|
+
const observed = UsageCreditsSchema.safeParse({
|
|
136
|
+
bucket: bucketId,
|
|
137
|
+
hasCredits: bucket.credits.hasCredits,
|
|
138
|
+
isUnlimited: bucket.credits.unlimited,
|
|
139
|
+
balance: bucket.credits.balance,
|
|
140
|
+
});
|
|
141
|
+
if (observed.success)
|
|
142
|
+
credits.push(observed.data);
|
|
143
|
+
}
|
|
128
144
|
for (const window of ['primary', 'secondary']) {
|
|
129
145
|
const entry = bucket[window];
|
|
130
146
|
if (entry)
|
|
@@ -137,8 +153,17 @@ export const toUsageSnapshot = (usage, observedAt) => {
|
|
|
137
153
|
remainingPercent: Math.max(0, 100 - entry.usedPercent),
|
|
138
154
|
durationMinutes: entry.windowDurationMins ?? null,
|
|
139
155
|
resetsAt: entry.resetsAt == null ? null : entry.resetsAt * 1000,
|
|
156
|
+
period: entry.windowDurationMins === 300
|
|
157
|
+
? 'five-hour'
|
|
158
|
+
: entry.windowDurationMins === 10080
|
|
159
|
+
? 'seven-day'
|
|
160
|
+
: 'other',
|
|
161
|
+
nativeKind: null,
|
|
162
|
+
group: null,
|
|
163
|
+
scope: null,
|
|
140
164
|
});
|
|
141
165
|
}
|
|
166
|
+
}
|
|
142
167
|
const reached = Object.values(buckets).some((bucket) => bucket.rateLimitReachedType != null);
|
|
143
168
|
const state = windows.length === 0
|
|
144
169
|
? 'unavailable'
|
|
@@ -152,5 +177,7 @@ export const toUsageSnapshot = (usage, observedAt) => {
|
|
|
152
177
|
observedAt,
|
|
153
178
|
ordinaryUsageAllowed: usage.ordinaryUsageAllowed ?? null,
|
|
154
179
|
windows,
|
|
180
|
+
credits: credits.length > 0 ? credits : null,
|
|
181
|
+
resetCredits: usage.rateLimitResetCredits ?? null,
|
|
155
182
|
});
|
|
156
183
|
};
|
|
@@ -99,9 +99,14 @@ export declare const NativeBucketSchema: z.ZodObject<{
|
|
|
99
99
|
resetsAt: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
100
100
|
}, z.core.$strip>>>;
|
|
101
101
|
rateLimitReachedType: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
102
|
+
credits: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
103
|
+
hasCredits: z.ZodBoolean;
|
|
104
|
+
unlimited: z.ZodBoolean;
|
|
105
|
+
balance: z.ZodNullable<z.ZodString>;
|
|
106
|
+
}, z.core.$strip>>>>;
|
|
102
107
|
}, z.core.$strip>;
|
|
103
108
|
export declare const NativeUsageSchema: z.ZodObject<{
|
|
104
|
-
rateLimitsByLimitId: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
109
|
+
rateLimitsByLimitId: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
105
110
|
limitId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
106
111
|
limitName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
107
112
|
primary: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
@@ -115,7 +120,35 @@ export declare const NativeUsageSchema: z.ZodObject<{
|
|
|
115
120
|
resetsAt: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
116
121
|
}, z.core.$strip>>>;
|
|
117
122
|
rateLimitReachedType: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
123
|
+
credits: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
124
|
+
hasCredits: z.ZodBoolean;
|
|
125
|
+
unlimited: z.ZodBoolean;
|
|
126
|
+
balance: z.ZodNullable<z.ZodString>;
|
|
127
|
+
}, z.core.$strip>>>>;
|
|
128
|
+
}, z.core.$strip>>>>;
|
|
129
|
+
rateLimits: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
130
|
+
limitId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
131
|
+
limitName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
132
|
+
primary: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
133
|
+
usedPercent: z.ZodNumber;
|
|
134
|
+
windowDurationMins: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
135
|
+
resetsAt: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
136
|
+
}, z.core.$strip>>>;
|
|
137
|
+
secondary: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
138
|
+
usedPercent: z.ZodNumber;
|
|
139
|
+
windowDurationMins: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
140
|
+
resetsAt: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
141
|
+
}, z.core.$strip>>>;
|
|
142
|
+
rateLimitReachedType: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
143
|
+
credits: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
144
|
+
hasCredits: z.ZodBoolean;
|
|
145
|
+
unlimited: z.ZodBoolean;
|
|
146
|
+
balance: z.ZodNullable<z.ZodString>;
|
|
147
|
+
}, z.core.$strip>>>>;
|
|
118
148
|
}, z.core.$strip>>>;
|
|
149
|
+
rateLimitResetCredits: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
150
|
+
availableCount: z.ZodNumber;
|
|
151
|
+
}, z.core.$strip>>>>;
|
|
119
152
|
ordinaryUsageAllowed: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
120
153
|
}, z.core.$strip>;
|
|
121
154
|
export type INativeThread = z.infer<typeof NativeThreadSchema>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { MAX_PAGE_SIZE, } from '../../contracts/index.js';
|
|
2
|
+
import { MAX_PAGE_SIZE, UsageCreditsSchema, UsageResetCreditsSchema, } from '../../contracts/index.js';
|
|
3
3
|
// additive native responses are narrowed to fields this adapter actually uses
|
|
4
4
|
export const NativeThreadSchema = z.object({
|
|
5
5
|
id: z.string().min(1).max(4096),
|
|
@@ -41,15 +41,24 @@ const NativeWindowSchema = z.object({
|
|
|
41
41
|
windowDurationMins: z.number().int().positive().nullable().optional(),
|
|
42
42
|
resetsAt: z.number().int().safe().nullable().optional(),
|
|
43
43
|
});
|
|
44
|
+
// optional billing metadata may be unavailable without invalidating measured quota windows
|
|
45
|
+
const NativeCreditsSchema = z.object({
|
|
46
|
+
hasCredits: UsageCreditsSchema.shape.hasCredits,
|
|
47
|
+
unlimited: UsageCreditsSchema.shape.isUnlimited,
|
|
48
|
+
balance: UsageCreditsSchema.shape.balance,
|
|
49
|
+
});
|
|
44
50
|
export const NativeBucketSchema = z.object({
|
|
45
51
|
limitId: z.string().nullable().optional(),
|
|
46
52
|
limitName: z.string().nullable().optional(),
|
|
47
53
|
primary: NativeWindowSchema.nullable().optional(),
|
|
48
54
|
secondary: NativeWindowSchema.nullable().optional(),
|
|
49
55
|
rateLimitReachedType: z.string().nullable().optional(),
|
|
56
|
+
credits: NativeCreditsSchema.nullable().optional().catch(null),
|
|
50
57
|
});
|
|
51
58
|
export const NativeUsageSchema = z.object({
|
|
52
|
-
rateLimitsByLimitId: z.record(z.string(), NativeBucketSchema).nullable(),
|
|
59
|
+
rateLimitsByLimitId: z.record(z.string(), NativeBucketSchema).nullable().optional(),
|
|
60
|
+
rateLimits: NativeBucketSchema.nullable().optional(),
|
|
61
|
+
rateLimitResetCredits: UsageResetCreditsSchema.nullable().optional().catch(null),
|
|
53
62
|
ordinaryUsageAllowed: z.boolean().nullable().optional(),
|
|
54
63
|
});
|
|
55
64
|
// native discovery is unpaged; limit inventory size and retain only display/invocation metadata
|
|
@@ -15,6 +15,8 @@ import { type IAgentHq, type IAgentHqOptions } from './types.js';
|
|
|
15
15
|
* - JG_AGENT_HQ_RUNTIME_NOT_RUNNING: The HQ runtime is not running
|
|
16
16
|
* - JG_AGENT_HQ_TRANSPORT_FAILED: The HQ transport failed
|
|
17
17
|
* - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
|
|
18
|
+
* - JG_AGENT_HQ_STORAGE_UPGRADE_REQUIRED: The HQ storage requires an explicit upgrade
|
|
19
|
+
* - JG_AGENT_HQ_STORAGE_VERSION_UNSUPPORTED: The HQ storage version is unsupported
|
|
18
20
|
* - JG_AGENT_HQ_STORAGE_INVALID: The HQ storage is invalid
|
|
19
21
|
*/
|
|
20
22
|
export declare const createAgentHq: (options: IAgentHqOptions) => Promise<IAgentHq>;
|
package/dist/runtime/runtime.js
CHANGED
|
@@ -39,6 +39,8 @@ const UnconfirmedCleanupSchema = z.object({ cleanupUnconfirmed: z.literal(true)
|
|
|
39
39
|
* - JG_AGENT_HQ_RUNTIME_NOT_RUNNING: The HQ runtime is not running
|
|
40
40
|
* - JG_AGENT_HQ_TRANSPORT_FAILED: The HQ transport failed
|
|
41
41
|
* - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
|
|
42
|
+
* - JG_AGENT_HQ_STORAGE_UPGRADE_REQUIRED: The HQ storage requires an explicit upgrade
|
|
43
|
+
* - JG_AGENT_HQ_STORAGE_VERSION_UNSUPPORTED: The HQ storage version is unsupported
|
|
42
44
|
* - JG_AGENT_HQ_STORAGE_INVALID: The HQ storage is invalid
|
|
43
45
|
*/
|
|
44
46
|
export const createAgentHq = async (options) => {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { prepareStateRoot, resolveAllowedRoots, isWithinWorkspace, resolveWorkspaceTarget, readRuntimeOwner, claimRuntimeOwner, prepareDatabase, prepareActionKey, } from './state.js';
|
|
1
|
+
export { prepareStateRoot, readExistingState, resolveAllowedRoots, isWithinWorkspace, resolveWorkspaceTarget, readRuntimeOwner, claimRuntimeOwner, prepareDatabase, prepareActionKey, } from './state.js';
|
|
2
2
|
export { RUNTIME_OWNER_FILE, DATABASE_FILE } from './constants.js';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// state ownership and trusted paths
|
|
2
|
-
export { prepareStateRoot, resolveAllowedRoots, isWithinWorkspace, resolveWorkspaceTarget, readRuntimeOwner, claimRuntimeOwner, prepareDatabase, prepareActionKey, } from './state.js';
|
|
2
|
+
export { prepareStateRoot, readExistingState, resolveAllowedRoots, isWithinWorkspace, resolveWorkspaceTarget, readRuntimeOwner, claimRuntimeOwner, prepareDatabase, prepareActionKey, } from './state.js';
|
|
3
3
|
export { RUNTIME_OWNER_FILE, DATABASE_FILE } from './constants.js';
|
|
@@ -56,7 +56,7 @@ export declare const readRuntimeOwner: (stateRoot: string) => Promise<{
|
|
|
56
56
|
* - JG_AGENT_HQ_RUNTIME_OWNER_UNVERIFIABLE: The HQ runtime owner is unverifiable
|
|
57
57
|
* - JG_AGENT_HQ_RUNTIME_NOT_RUNNING: The HQ runtime is not running
|
|
58
58
|
*/
|
|
59
|
-
export declare const claimRuntimeOwner: (stateRoot: string) => Promise<{
|
|
59
|
+
export declare const claimRuntimeOwner: (stateRoot: string, command?: "serve" | "upgrade-storage") => Promise<{
|
|
60
60
|
owner: {
|
|
61
61
|
pid: number;
|
|
62
62
|
nonce: string;
|
|
@@ -72,3 +72,18 @@ export declare const claimRuntimeOwner: (stateRoot: string) => Promise<{
|
|
|
72
72
|
* - JG_AGENT_HQ_STATE_FILE_UNSAFE: The HQ state file is unsafe
|
|
73
73
|
*/
|
|
74
74
|
export declare const prepareDatabase: (stateRoot: string) => Promise<string>;
|
|
75
|
+
/**
|
|
76
|
+
* Validates existing offline storage without creating directories, database files or keys.
|
|
77
|
+
* @returns Canonical state and the exact existing private key.
|
|
78
|
+
* @throws
|
|
79
|
+
* - JG_AGENT_HQ_LOCK_UNSUPPORTED_PLATFORM: The HQ lock requires Linux or macOS
|
|
80
|
+
* - JG_AGENT_HQ_REQUEST_INVALID: The HQ request is invalid
|
|
81
|
+
* - JG_AGENT_HQ_STATE_DIRECTORY_UNSAFE: The HQ state directory is unsafe
|
|
82
|
+
* - JG_AGENT_HQ_STATE_FILE_UNSAFE: The HQ state file is unsafe
|
|
83
|
+
* - JG_AGENT_HQ_ACTION_KEY_UNAVAILABLE: The HQ action key is unavailable
|
|
84
|
+
*/
|
|
85
|
+
export declare const readExistingState: (stateRoot: string) => Promise<{
|
|
86
|
+
stateRoot: string;
|
|
87
|
+
databasePath: string;
|
|
88
|
+
actionKey: Uint8Array;
|
|
89
|
+
}>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { constants } from 'node:fs';
|
|
2
2
|
import { lstat, open, realpath, rm } from 'node:fs/promises';
|
|
3
|
-
import { isAbsolute, join, relative, sep } from 'node:path';
|
|
3
|
+
import { isAbsolute, join, relative, sep, parse, resolve } from 'node:path';
|
|
4
4
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
5
|
+
import { assertNoLinksBelow } from 'jg-fs';
|
|
5
6
|
import { Exception, extractMessage, hasErrorCode, isException } from 'jg-exception';
|
|
6
7
|
import { ensurePrivateRuntimeDirectory, HqLockOwnerSchema, isPrivateOwner, } from '../../locks/index.js';
|
|
7
8
|
import { ACTION_KEY_FILE, DATABASE_FILE, RUNTIME_OWNER_FILE } from './constants.js';
|
|
@@ -215,11 +216,11 @@ export const readRuntimeOwner = async (stateRoot) => {
|
|
|
215
216
|
* - JG_AGENT_HQ_RUNTIME_OWNER_UNVERIFIABLE: The HQ runtime owner is unverifiable
|
|
216
217
|
* - JG_AGENT_HQ_RUNTIME_NOT_RUNNING: The HQ runtime is not running
|
|
217
218
|
*/
|
|
218
|
-
export const claimRuntimeOwner = async (stateRoot) => {
|
|
219
|
+
export const claimRuntimeOwner = async (stateRoot, command = 'serve') => {
|
|
219
220
|
const owner = {
|
|
220
221
|
pid: process.pid,
|
|
221
222
|
nonce: randomUUID(),
|
|
222
|
-
command
|
|
223
|
+
command,
|
|
223
224
|
createdAt: new Date().toISOString(),
|
|
224
225
|
};
|
|
225
226
|
const content = `${JSON.stringify(owner)}\n`;
|
|
@@ -294,3 +295,93 @@ export const prepareDatabase = async (stateRoot) => {
|
|
|
294
295
|
}
|
|
295
296
|
return path;
|
|
296
297
|
};
|
|
298
|
+
/**
|
|
299
|
+
* Validates existing offline storage without creating directories, database files or keys.
|
|
300
|
+
* @returns Canonical state and the exact existing private key.
|
|
301
|
+
* @throws
|
|
302
|
+
* - JG_AGENT_HQ_LOCK_UNSUPPORTED_PLATFORM: The HQ lock requires Linux or macOS
|
|
303
|
+
* - JG_AGENT_HQ_REQUEST_INVALID: The HQ request is invalid
|
|
304
|
+
* - JG_AGENT_HQ_STATE_DIRECTORY_UNSAFE: The HQ state directory is unsafe
|
|
305
|
+
* - JG_AGENT_HQ_STATE_FILE_UNSAFE: The HQ state file is unsafe
|
|
306
|
+
* - JG_AGENT_HQ_ACTION_KEY_UNAVAILABLE: The HQ action key is unavailable
|
|
307
|
+
*/
|
|
308
|
+
export const readExistingState = async (stateRoot) => {
|
|
309
|
+
if (process.platform !== 'linux' && process.platform !== 'darwin')
|
|
310
|
+
throw new Exception('The HQ lock requires Linux or macOS', {
|
|
311
|
+
code: 'JG_AGENT_HQ_LOCK_UNSUPPORTED_PLATFORM',
|
|
312
|
+
});
|
|
313
|
+
if (!isAbsolute(stateRoot) || resolve(stateRoot) !== stateRoot)
|
|
314
|
+
throw new Exception('The HQ request is invalid', {
|
|
315
|
+
code: 'JG_AGENT_HQ_REQUEST_INVALID',
|
|
316
|
+
data: { kind: 'invalid-request' },
|
|
317
|
+
});
|
|
318
|
+
try {
|
|
319
|
+
await assertNoLinksBelow(parse(stateRoot).root, stateRoot);
|
|
320
|
+
const directory = await lstat(stateRoot);
|
|
321
|
+
if (!directory.isDirectory() ||
|
|
322
|
+
!isPrivateOwner(directory) ||
|
|
323
|
+
(await realpath(stateRoot)) !== stateRoot)
|
|
324
|
+
throw new Exception('The HQ state directory is unsafe', {
|
|
325
|
+
code: 'JG_AGENT_HQ_STATE_DIRECTORY_UNSAFE',
|
|
326
|
+
data: { kind: 'state-conflict' },
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
catch (cause) {
|
|
330
|
+
if (hasErrorCode(cause, 'JG_AGENT_HQ_STATE_DIRECTORY_UNSAFE'))
|
|
331
|
+
throw cause;
|
|
332
|
+
throw new Exception(`The HQ state directory is unsafe: ${extractMessage(cause)}`, {
|
|
333
|
+
code: 'JG_AGENT_HQ_STATE_DIRECTORY_UNSAFE',
|
|
334
|
+
data: { kind: 'state-conflict' },
|
|
335
|
+
cause,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
const databasePath = join(stateRoot, DATABASE_FILE);
|
|
339
|
+
for (const filename of [
|
|
340
|
+
databasePath,
|
|
341
|
+
`${databasePath}-wal`,
|
|
342
|
+
`${databasePath}-shm`,
|
|
343
|
+
`${databasePath}-journal`,
|
|
344
|
+
]) {
|
|
345
|
+
try {
|
|
346
|
+
const details = await lstat(filename);
|
|
347
|
+
if (!details.isFile() ||
|
|
348
|
+
details.isSymbolicLink() ||
|
|
349
|
+
details.nlink !== 1 ||
|
|
350
|
+
!isPrivateOwner(details))
|
|
351
|
+
throw new Exception('The HQ state file is unsafe', {
|
|
352
|
+
code: 'JG_AGENT_HQ_STATE_FILE_UNSAFE',
|
|
353
|
+
data: { kind: 'state-conflict' },
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
catch (cause) {
|
|
357
|
+
if (filename !== databasePath && hasErrorCode(cause, 'ENOENT'))
|
|
358
|
+
continue;
|
|
359
|
+
if (hasErrorCode(cause, 'JG_AGENT_HQ_STATE_FILE_UNSAFE'))
|
|
360
|
+
throw cause;
|
|
361
|
+
throw new Exception(`The HQ state file is unsafe: ${extractMessage(cause)}`, {
|
|
362
|
+
code: 'JG_AGENT_HQ_STATE_FILE_UNSAFE',
|
|
363
|
+
data: { kind: 'state-conflict' },
|
|
364
|
+
cause,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
let actionKey;
|
|
369
|
+
try {
|
|
370
|
+
actionKey = await readPrivateBytes(join(stateRoot, ACTION_KEY_FILE), 32);
|
|
371
|
+
}
|
|
372
|
+
catch (cause) {
|
|
373
|
+
if (isException(cause))
|
|
374
|
+
throw cause;
|
|
375
|
+
throw new Exception(`The HQ action key is unavailable: ${extractMessage(cause)}`, {
|
|
376
|
+
code: 'JG_AGENT_HQ_ACTION_KEY_UNAVAILABLE',
|
|
377
|
+
data: { kind: 'state-conflict' },
|
|
378
|
+
cause,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
if (actionKey.length !== 32)
|
|
382
|
+
throw new Exception('The HQ action key is unavailable', {
|
|
383
|
+
code: 'JG_AGENT_HQ_ACTION_KEY_UNAVAILABLE',
|
|
384
|
+
data: { kind: 'state-conflict' },
|
|
385
|
+
});
|
|
386
|
+
return { stateRoot, databasePath, actionKey };
|
|
387
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ISnapshotSource, type IHqSnapshotInspection } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Streams the shared format-1 capture and verifies it before transferring destination ownership.
|
|
4
|
+
* The caller must hold its database/metadata checkpoint; no cache or full inventory is retained.
|
|
5
|
+
* @returns The fully verified snapshot, retained by the caller after this resolves.
|
|
6
|
+
* @throws
|
|
7
|
+
* - JG_AGENT_HQ_STORE_FAILED: The HQ store failed
|
|
8
|
+
* - JG_PLANS_LOCK_BUSY: The plan document is locked
|
|
9
|
+
* - JG_PLANS_LOCK_FAILED: The plan document lock operation failed
|
|
10
|
+
* - JG_AGENT_HQ_SNAPSHOT_INVALID: The HQ snapshot is invalid
|
|
11
|
+
* - JG_AGENT_HQ_SNAPSHOT_FAILED: The HQ snapshot operation failed
|
|
12
|
+
* - JG_AGENT_HQ_STORAGE_VERSION_UNSUPPORTED: The HQ storage version is unsupported
|
|
13
|
+
*/
|
|
14
|
+
export declare const captureSnapshot: (context: ISnapshotSource, destination: string) => Promise<IHqSnapshotInspection>;
|