@gajae-code/coding-agent 0.12.6 → 0.12.7
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 +3 -1
- package/dist/types/async/job-manager.d.ts +36 -1
- package/dist/types/eval/backend.d.ts +2 -2
- package/dist/types/exec/bash-executor.d.ts +8 -10
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +0 -36
- package/dist/types/internal-urls/registry-helpers.d.ts +0 -18
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +1 -3
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +2 -4
- package/dist/types/sdk/host/host.d.ts +2 -23
- package/dist/types/sdk/host/reverse-leases.d.ts +4 -7
- package/dist/types/session/artifacts.d.ts +5 -8
- package/dist/types/session/client-bridge.d.ts +0 -14
- package/dist/types/session/internal/managed-session-storage.d.ts +2 -5
- package/dist/types/session/messages.d.ts +1 -4
- package/dist/types/session/session-manager.d.ts +4 -5
- package/dist/types/session/streaming-output.d.ts +0 -32
- package/dist/types/task/receipt.d.ts +1 -2
- package/dist/types/task/types.d.ts +43 -178
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/fetch.d.ts +0 -3
- package/dist/types/tools/output-meta.d.ts +5 -37
- package/dist/types/tools/subagent.d.ts +15 -0
- package/dist/types/tools/todo-write.d.ts +3 -0
- package/dist/types/tools/tool-result.d.ts +0 -1
- package/package.json +8 -8
- package/src/async/job-manager.ts +430 -46
- package/src/cursor.ts +13 -20
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -3
- package/src/eval/backend.ts +2 -2
- package/src/exec/bash-executor.ts +151 -425
- package/src/gjc-runtime/ultragoal-runtime.ts +3 -280
- package/src/gjc-runtime/workflow-manifest.generated.json +1 -40
- package/src/gjc-runtime/workflow-manifest.ts +0 -12
- package/src/internal-urls/agent-protocol.ts +38 -98
- package/src/internal-urls/artifact-protocol.ts +17 -28
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/internal-urls/registry-helpers.ts +0 -62
- package/src/modes/acp/acp-agent.ts +0 -24
- package/src/modes/acp/acp-client-bridge.ts +11 -154
- package/src/prompts/tools/bash.md +2 -2
- package/src/sdk/acp/adapter.ts +9 -200
- package/src/sdk/bus/chat-daemon-control.ts +3 -5
- package/src/sdk/bus/index.ts +8 -324
- package/src/sdk/bus/telegram-daemon-contract.ts +2 -4
- package/src/sdk/host/host.ts +9 -187
- package/src/sdk/host/reverse-leases.ts +42 -249
- package/src/session/agent-session.ts +15 -178
- package/src/session/artifacts.ts +74 -211
- package/src/session/client-bridge.ts +0 -15
- package/src/session/internal/managed-session-storage.ts +5 -104
- package/src/session/messages.ts +7 -32
- package/src/session/session-manager.ts +21 -12
- package/src/session/streaming-output.ts +40 -198
- package/src/task/index.ts +230 -64
- package/src/task/receipt.ts +2 -4
- package/src/task/render.ts +0 -30
- package/src/task/types.ts +10 -34
- package/src/tools/bash.ts +239 -818
- package/src/tools/eval.ts +4 -99
- package/src/tools/fetch.ts +15 -77
- package/src/tools/find.ts +3 -7
- package/src/tools/output-meta.ts +114 -696
- package/src/tools/search.ts +4 -9
- package/src/tools/subagent.ts +97 -75
- package/src/tools/todo-write.ts +47 -0
- package/src/tools/tool-result.ts +0 -1
- package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +0 -83
- package/dist/types/utils/artifact-id.d.ts +0 -2
- package/src/gjc-runtime/ultragoal-review-source.ts +0 -378
- package/src/utils/artifact-id.ts +0 -6
|
@@ -77,17 +77,6 @@ export {
|
|
|
77
77
|
validateRecoveryAdmission,
|
|
78
78
|
validateRecoveryPath,
|
|
79
79
|
} from "./ultragoal-owner-loss-recovery";
|
|
80
|
-
|
|
81
|
-
import {
|
|
82
|
-
captureReviewSourceSnapshot,
|
|
83
|
-
createReviewSourceCohort,
|
|
84
|
-
createReviewSourceDispatch,
|
|
85
|
-
normalizeReviewSourceCohorts,
|
|
86
|
-
type ReviewDeliveryDisposition,
|
|
87
|
-
type ReviewSourceCohort,
|
|
88
|
-
type ReviewSourceLane,
|
|
89
|
-
} from "./ultragoal-review-source";
|
|
90
|
-
|
|
91
80
|
export type UltragoalGjcGoalMode = "aggregate" | "per-story";
|
|
92
81
|
export type UltragoalGoalStatus =
|
|
93
82
|
| "pending"
|
|
@@ -138,8 +127,6 @@ export interface UltragoalPlan {
|
|
|
138
127
|
goals: UltragoalGoal[];
|
|
139
128
|
/** Authoritative repository identity for multi-repo fail-closed spawn (#2901). */
|
|
140
129
|
repositoryBinding?: RepositoryBinding;
|
|
141
|
-
reviewCohorts?: ReviewSourceCohort[];
|
|
142
|
-
|
|
143
130
|
createdAt: string;
|
|
144
131
|
updatedAt: string;
|
|
145
132
|
[key: string]: unknown;
|
|
@@ -997,8 +984,6 @@ function normalizePlan(raw: unknown): UltragoalPlan {
|
|
|
997
984
|
if (record.repositoryBinding !== undefined) {
|
|
998
985
|
repositoryBinding = parseRepositoryBinding(record.repositoryBinding);
|
|
999
986
|
}
|
|
1000
|
-
const reviewCohorts = normalizeReviewSourceCohorts(record.reviewCohorts);
|
|
1001
|
-
|
|
1002
987
|
return {
|
|
1003
988
|
version: 1,
|
|
1004
989
|
brief,
|
|
@@ -1009,7 +994,6 @@ function normalizePlan(raw: unknown): UltragoalPlan {
|
|
|
1009
994
|
createdAt,
|
|
1010
995
|
updatedAt,
|
|
1011
996
|
...(repositoryBinding ? { repositoryBinding } : {}),
|
|
1012
|
-
reviewCohorts,
|
|
1013
997
|
...(typeof record.state_revision === "number" && Number.isFinite(record.state_revision)
|
|
1014
998
|
? { state_revision: record.state_revision }
|
|
1015
999
|
: {}),
|
|
@@ -2530,12 +2514,7 @@ const COHORT_LANE_KEYS = ["cleaner", "architect", "qa"] as const;
|
|
|
2530
2514
|
* generations are delta-only. Cohort state rides the existing `iteration` gate key so
|
|
2531
2515
|
* no new top-level quality-gate key is introduced.
|
|
2532
2516
|
*/
|
|
2533
|
-
|
|
2534
|
-
cwd: string,
|
|
2535
|
-
gate: JsonObject,
|
|
2536
|
-
iteration: JsonObject,
|
|
2537
|
-
plan?: UltragoalPlan,
|
|
2538
|
-
): Promise<void> {
|
|
2517
|
+
function validateReviewCohort(gate: JsonObject, iteration: JsonObject): void {
|
|
2539
2518
|
const cohort = qualityGateObject(iteration.reviewCohort);
|
|
2540
2519
|
if (!cohort) throw new Error("qualityGate iteration.reviewCohort is required at the review boundary");
|
|
2541
2520
|
const generation = cohort.reviewGeneration;
|
|
@@ -2543,40 +2522,6 @@ async function validateReviewCohort(
|
|
|
2543
2522
|
throw new Error("iteration.reviewCohort.reviewGeneration must be an integer >= 1");
|
|
2544
2523
|
const sourceHash = nonEmptyString(cohort.sourceHash);
|
|
2545
2524
|
if (!sourceHash) throw new Error("iteration.reviewCohort.sourceHash is required");
|
|
2546
|
-
const cohortId = nonEmptyString(cohort.cohortId);
|
|
2547
|
-
const lanesForAuthority = qualityGateObject(cohort.lanes);
|
|
2548
|
-
const deliveryBearing = COHORT_LANE_KEYS.some(lane =>
|
|
2549
|
-
nonEmptyString(qualityGateObject(lanesForAuthority?.[lane])?.deliveryId),
|
|
2550
|
-
);
|
|
2551
|
-
const authoritativeReview = Boolean(cohortId || deliveryBearing || plan?.reviewCohorts?.length);
|
|
2552
|
-
if (authoritativeReview && !cohortId) {
|
|
2553
|
-
throw new Error(
|
|
2554
|
-
"iteration.reviewCohort.cohortId is required; reviewer-declared source hashes are not gate authority",
|
|
2555
|
-
);
|
|
2556
|
-
}
|
|
2557
|
-
const authoritative = cohortId ? plan?.reviewCohorts?.find(record => record.cohortId === cohortId) : undefined;
|
|
2558
|
-
if (cohortId && !authoritative) {
|
|
2559
|
-
throw new Error("iteration.reviewCohort.cohortId must resolve to a runtime-owned persisted review cohort");
|
|
2560
|
-
}
|
|
2561
|
-
if (authoritative?.status !== undefined && authoritative.status !== "active") {
|
|
2562
|
-
throw new Error(
|
|
2563
|
-
"iteration.reviewCohort references a superseded review cohort; rerun reviews on the active source snapshot",
|
|
2564
|
-
);
|
|
2565
|
-
}
|
|
2566
|
-
if (authoritative && (authoritative.generation !== generation || authoritative.snapshotId !== sourceHash)) {
|
|
2567
|
-
throw new Error(
|
|
2568
|
-
"iteration.reviewCohort generation/sourceHash must match the runtime-owned persisted review cohort",
|
|
2569
|
-
);
|
|
2570
|
-
}
|
|
2571
|
-
if (authoritative && plan?.repositoryBinding) {
|
|
2572
|
-
const current = await captureReviewSourceSnapshot(cwd, plan.repositoryBinding);
|
|
2573
|
-
if (current.snapshotId !== authoritative.snapshotId) {
|
|
2574
|
-
throw new Error(
|
|
2575
|
-
"iteration.reviewCohort source advanced after review delivery; rerun the cohort on the current source snapshot",
|
|
2576
|
-
);
|
|
2577
|
-
}
|
|
2578
|
-
}
|
|
2579
|
-
|
|
2580
2525
|
if (cohort.joined !== true)
|
|
2581
2526
|
throw new Error("iteration.reviewCohort.joined must be true: all lane findings must join before checkpoint");
|
|
2582
2527
|
const lanes = qualityGateObject(cohort.lanes);
|
|
@@ -2589,22 +2534,6 @@ async function validateReviewCohort(
|
|
|
2589
2534
|
throw new Error(`iteration.reviewCohort.lanes.${lane} must be one lane per generation, not a list`);
|
|
2590
2535
|
const record = qualityGateObject(lanes[lane]);
|
|
2591
2536
|
if (!record) throw new Error(`iteration.reviewCohort.lanes.${lane} is required`);
|
|
2592
|
-
if (authoritative) {
|
|
2593
|
-
const deliveryId = nonEmptyString(record.deliveryId);
|
|
2594
|
-
if (!deliveryId) throw new Error(`iteration.reviewCohort.lanes.${lane}.deliveryId is required`);
|
|
2595
|
-
const delivery = authoritative.deliveries.find(item => item.deliveryId === deliveryId && item.lane === lane);
|
|
2596
|
-
if (!delivery) {
|
|
2597
|
-
throw new Error(
|
|
2598
|
-
`iteration.reviewCohort.lanes.${lane}.deliveryId must resolve to its runtime-owned lane delivery`,
|
|
2599
|
-
);
|
|
2600
|
-
}
|
|
2601
|
-
if (delivery.disposition !== "current" || delivery.snapshotId !== sourceHash) {
|
|
2602
|
-
throw new Error(
|
|
2603
|
-
`iteration.reviewCohort.lanes.${lane} is ${delivery.disposition}; stale or invalid review delivery cannot satisfy the current gate. Rerun the ${lane} lane on cohort ${cohortId}`,
|
|
2604
|
-
);
|
|
2605
|
-
}
|
|
2606
|
-
}
|
|
2607
|
-
|
|
2608
2537
|
const laneHash = nonEmptyString(record.sourceHash);
|
|
2609
2538
|
if (!laneHash) throw new Error(`iteration.reviewCohort.lanes.${lane}.sourceHash is required`);
|
|
2610
2539
|
if (laneHash !== sourceHash)
|
|
@@ -2642,19 +2571,6 @@ async function validateReviewCohort(
|
|
|
2642
2571
|
// per-lane or per-generation vote.
|
|
2643
2572
|
const critic = qualityGateObject(gate.criticReview);
|
|
2644
2573
|
if (critic) {
|
|
2645
|
-
if (authoritative) {
|
|
2646
|
-
const criticDeliveryId = nonEmptyString(critic.deliveryId);
|
|
2647
|
-
if (!criticDeliveryId) throw new Error("criticReview.deliveryId is required for the terminal review gate");
|
|
2648
|
-
const criticDelivery = authoritative.deliveries.find(
|
|
2649
|
-
item => item.deliveryId === criticDeliveryId && item.lane === "critic",
|
|
2650
|
-
);
|
|
2651
|
-
if (criticDelivery?.disposition !== "current" || criticDelivery.snapshotId !== sourceHash) {
|
|
2652
|
-
throw new Error(
|
|
2653
|
-
"criticReview.deliveryId must resolve to a current terminal critic delivery on the joined cohort",
|
|
2654
|
-
);
|
|
2655
|
-
}
|
|
2656
|
-
}
|
|
2657
|
-
|
|
2658
2574
|
const criticHash = nonEmptyString(critic.sourceHash);
|
|
2659
2575
|
if (criticHash && criticHash !== sourceHash)
|
|
2660
2576
|
throw new Error(
|
|
@@ -2852,9 +2768,7 @@ async function validateCompletionQualityGate(
|
|
|
2852
2768
|
found.check("iteration.blockers", "non_empty_blockers", () =>
|
|
2853
2769
|
requireEmptyBlockers(iteration.blockers, "iteration.blockers"),
|
|
2854
2770
|
);
|
|
2855
|
-
|
|
2856
|
-
validateReviewCohort(cwd, gate, iteration, options.plan),
|
|
2857
|
-
);
|
|
2771
|
+
found.check("iteration.reviewCohort", "review_cohort_invalid", () => validateReviewCohort(gate, iteration));
|
|
2858
2772
|
if (batchMode && options.goal && options.plan && options.ledger) {
|
|
2859
2773
|
found.check("validationBatchClose", "batch_close_invalid", () =>
|
|
2860
2774
|
validateBatchCloseQualityGate(gate, options.plan!, batchMode, options.ledger!, options.changeSet),
|
|
@@ -4003,150 +3917,9 @@ async function annotateUltragoalLedger(input: {
|
|
|
4003
3917
|
rationale: input.rationale,
|
|
4004
3918
|
});
|
|
4005
3919
|
await appendLedger(input.cwd, { event: "steering_accepted", kind, evidence, rationale });
|
|
4006
|
-
|
|
4007
3920
|
return { plan: input.plan };
|
|
4008
3921
|
}
|
|
4009
3922
|
|
|
4010
|
-
async function mutateReviewCohorts<T>(cwd: string, mutate: (plan: UltragoalPlan) => T): Promise<T> {
|
|
4011
|
-
for (let attempt = 0; attempt < 5; attempt++) {
|
|
4012
|
-
const plan = await readUltragoalPlan(cwd);
|
|
4013
|
-
if (!plan) throw new Error("No ultragoal plan found");
|
|
4014
|
-
const result = mutate(plan);
|
|
4015
|
-
plan.updatedAt = new Date().toISOString();
|
|
4016
|
-
try {
|
|
4017
|
-
await writePlan(cwd, plan);
|
|
4018
|
-
return result;
|
|
4019
|
-
} catch (error) {
|
|
4020
|
-
if (!(error instanceof Error) || !error.message.includes("revision")) throw error;
|
|
4021
|
-
}
|
|
4022
|
-
}
|
|
4023
|
-
throw new Error("review cohort state changed concurrently; rerun the operation");
|
|
4024
|
-
}
|
|
4025
|
-
|
|
4026
|
-
export async function validateUltragoalReviewDispatch(input: {
|
|
4027
|
-
cwd: string;
|
|
4028
|
-
dispatchId: string;
|
|
4029
|
-
cohortId: string;
|
|
4030
|
-
taskId: string;
|
|
4031
|
-
lane: ReviewSourceLane;
|
|
4032
|
-
snapshotId: string;
|
|
4033
|
-
generation: number;
|
|
4034
|
-
repositoryBindingDigest: string;
|
|
4035
|
-
stateRevision: number;
|
|
4036
|
-
rerunCommand: string;
|
|
4037
|
-
taskSourceTaskId: string;
|
|
4038
|
-
createdAt: string;
|
|
4039
|
-
}): Promise<void> {
|
|
4040
|
-
const plan = await readUltragoalPlan(input.cwd);
|
|
4041
|
-
const cohort = plan?.reviewCohorts?.find(item => item.cohortId === input.cohortId && item.status === "active");
|
|
4042
|
-
const dispatch = cohort?.dispatches.find(item => item.dispatchId === input.dispatchId);
|
|
4043
|
-
if (
|
|
4044
|
-
!dispatch ||
|
|
4045
|
-
dispatch.taskId !== input.taskId ||
|
|
4046
|
-
dispatch.lane !== input.lane ||
|
|
4047
|
-
dispatch.snapshotId !== input.snapshotId ||
|
|
4048
|
-
dispatch.generation !== input.generation ||
|
|
4049
|
-
dispatch.repositoryBindingDigest !== input.repositoryBindingDigest ||
|
|
4050
|
-
dispatch.stateRevision !== input.stateRevision ||
|
|
4051
|
-
dispatch.rerunCommand !== input.rerunCommand ||
|
|
4052
|
-
dispatch.taskId !== input.taskSourceTaskId ||
|
|
4053
|
-
dispatch.createdAt !== input.createdAt
|
|
4054
|
-
) {
|
|
4055
|
-
throw new Error("reviewSource must resolve to an active runtime-owned dispatch before task launch");
|
|
4056
|
-
}
|
|
4057
|
-
}
|
|
4058
|
-
|
|
4059
|
-
export async function freezeUltragoalReviewCohort(input: {
|
|
4060
|
-
cwd: string;
|
|
4061
|
-
workflow?: "ultragoal" | "ralplan";
|
|
4062
|
-
}): Promise<ReviewSourceCohort> {
|
|
4063
|
-
const plan = await readUltragoalPlan(input.cwd);
|
|
4064
|
-
if (!plan?.repositoryBinding) throw new Error("review cohort freeze requires an authoritative repository binding");
|
|
4065
|
-
const captured = await captureReviewSourceSnapshot(input.cwd, plan.repositoryBinding);
|
|
4066
|
-
const prior = plan.reviewCohorts?.find(cohort => cohort.status === "active");
|
|
4067
|
-
if (
|
|
4068
|
-
prior?.snapshotId === captured.snapshotId &&
|
|
4069
|
-
prior.repositoryBindingDigest === captured.repositoryBindingDigest
|
|
4070
|
-
) {
|
|
4071
|
-
return prior;
|
|
4072
|
-
}
|
|
4073
|
-
const cohort = createReviewSourceCohort({
|
|
4074
|
-
workflow: input.workflow ?? "ultragoal",
|
|
4075
|
-
generation: (prior?.generation ?? 0) + 1,
|
|
4076
|
-
snapshotId: captured.snapshotId,
|
|
4077
|
-
repositoryBindingDigest: captured.repositoryBindingDigest,
|
|
4078
|
-
stateRevision: persistedStateRevision(plan),
|
|
4079
|
-
});
|
|
4080
|
-
if (prior) {
|
|
4081
|
-
prior.status = "superseded";
|
|
4082
|
-
prior.supersededBy = cohort.cohortId;
|
|
4083
|
-
}
|
|
4084
|
-
plan.reviewCohorts = [...(plan.reviewCohorts ?? []), cohort];
|
|
4085
|
-
plan.updatedAt = new Date().toISOString();
|
|
4086
|
-
await writePlan(input.cwd, plan);
|
|
4087
|
-
return cohort;
|
|
4088
|
-
}
|
|
4089
|
-
|
|
4090
|
-
export async function dispatchUltragoalReviewLane(input: {
|
|
4091
|
-
cwd: string;
|
|
4092
|
-
cohortId: string;
|
|
4093
|
-
taskId: string;
|
|
4094
|
-
lane: ReviewSourceLane;
|
|
4095
|
-
rerunCommand: string;
|
|
4096
|
-
}): Promise<ReturnType<typeof createReviewSourceDispatch>> {
|
|
4097
|
-
return mutateReviewCohorts(input.cwd, plan => {
|
|
4098
|
-
const cohort = plan.reviewCohorts?.find(item => item.cohortId === input.cohortId);
|
|
4099
|
-
if (!cohort) throw new Error(`Unknown review cohort ${input.cohortId}`);
|
|
4100
|
-
const dispatch = createReviewSourceDispatch({
|
|
4101
|
-
cohort,
|
|
4102
|
-
taskId: input.taskId,
|
|
4103
|
-
lane: input.lane,
|
|
4104
|
-
rerunCommand: input.rerunCommand,
|
|
4105
|
-
});
|
|
4106
|
-
cohort.dispatches.push(dispatch);
|
|
4107
|
-
return dispatch;
|
|
4108
|
-
});
|
|
4109
|
-
}
|
|
4110
|
-
|
|
4111
|
-
export async function classifyUltragoalReviewDelivery(input: {
|
|
4112
|
-
cwd: string;
|
|
4113
|
-
cohortId: string;
|
|
4114
|
-
dispatchId: string;
|
|
4115
|
-
observedDisposition: ReviewDeliveryDisposition;
|
|
4116
|
-
}): Promise<{ disposition: ReviewDeliveryDisposition; deliveryId: string }> {
|
|
4117
|
-
return mutateReviewCohorts(input.cwd, plan => {
|
|
4118
|
-
const cohort = plan.reviewCohorts?.find(item => item.cohortId === input.cohortId);
|
|
4119
|
-
const active = plan.reviewCohorts?.find(item => item.status === "active");
|
|
4120
|
-
if (!cohort) throw new Error(`Unknown review cohort ${input.cohortId}`);
|
|
4121
|
-
const dispatch = cohort.dispatches.find(item => item.dispatchId === input.dispatchId);
|
|
4122
|
-
if (!dispatch) throw new Error(`Unknown review dispatch ${input.dispatchId}`);
|
|
4123
|
-
const disposition =
|
|
4124
|
-
input.observedDisposition === "invalid_provenance"
|
|
4125
|
-
? "invalid_provenance"
|
|
4126
|
-
: active?.cohortId === cohort.cohortId && cohort.status === "active"
|
|
4127
|
-
? input.observedDisposition
|
|
4128
|
-
: "stale_review_delivery";
|
|
4129
|
-
const existing = cohort.deliveries.find(item => item.dispatchId === dispatch.dispatchId);
|
|
4130
|
-
if (existing) {
|
|
4131
|
-
if (existing.disposition !== disposition) throw new Error("conflicting_review_delivery");
|
|
4132
|
-
return { disposition, deliveryId: existing.deliveryId };
|
|
4133
|
-
}
|
|
4134
|
-
const deliveryId = crypto.randomUUID();
|
|
4135
|
-
cohort.deliveries.push({
|
|
4136
|
-
deliveryId,
|
|
4137
|
-
cohortId: cohort.cohortId,
|
|
4138
|
-
dispatchId: dispatch.dispatchId,
|
|
4139
|
-
taskId: dispatch.taskId,
|
|
4140
|
-
lane: dispatch.lane,
|
|
4141
|
-
snapshotId: dispatch.snapshotId,
|
|
4142
|
-
disposition,
|
|
4143
|
-
receivedAt: new Date().toISOString(),
|
|
4144
|
-
rerunCommand: dispatch.rerunCommand,
|
|
4145
|
-
});
|
|
4146
|
-
return { disposition, deliveryId };
|
|
4147
|
-
});
|
|
4148
|
-
}
|
|
4149
|
-
|
|
4150
3923
|
async function markBlockedUltragoalSuperseded(input: {
|
|
4151
3924
|
cwd: string;
|
|
4152
3925
|
plan: UltragoalPlan;
|
|
@@ -4948,21 +4721,6 @@ function renderUltragoalHelp(args: readonly string[]): string | null {
|
|
|
4948
4721
|
].join("\n");
|
|
4949
4722
|
}
|
|
4950
4723
|
|
|
4951
|
-
if (subject === "review-source") {
|
|
4952
|
-
return [
|
|
4953
|
-
"Run native GJC Ultragoal workflow commands",
|
|
4954
|
-
"",
|
|
4955
|
-
"USAGE",
|
|
4956
|
-
" $ gjc ultragoal review-source freeze [--json]",
|
|
4957
|
-
" $ gjc ultragoal review-source dispatch --cohort-id <id> --task-id <id> --lane <cleaner|architect|qa|critic> --rerun-command <command> [--json]",
|
|
4958
|
-
"",
|
|
4959
|
-
"DESCRIPTION",
|
|
4960
|
-
" Freeze derives and persists a runtime-owned source snapshot before review dispatch.",
|
|
4961
|
-
" Dispatch returns the leader-issued reviewSource object required by source-aware task lanes.",
|
|
4962
|
-
"",
|
|
4963
|
-
].join("\n");
|
|
4964
|
-
}
|
|
4965
|
-
|
|
4966
4724
|
if (subject === "quality-gate") {
|
|
4967
4725
|
return [
|
|
4968
4726
|
"Run native GJC Ultragoal workflow commands",
|
|
@@ -5004,11 +4762,9 @@ function renderUltragoalHelp(args: readonly string[]): string | null {
|
|
|
5004
4762
|
" record-critic-gate-override",
|
|
5005
4763
|
" quality-gate init",
|
|
5006
4764
|
" quality-gate validate",
|
|
5007
|
-
" review-source freeze",
|
|
5008
|
-
" review-source dispatch",
|
|
5009
4765
|
|
|
5010
4766
|
"",
|
|
5011
|
-
"Run `gjc ultragoal checkpoint --help`, `gjc ultragoal review --help`, `gjc ultragoal
|
|
4767
|
+
"Run `gjc ultragoal checkpoint --help`, `gjc ultragoal review --help`, `gjc ultragoal classify-blocker --help`, `gjc ultragoal record-critic-verdict --help`, or `gjc ultragoal record-critic-gate-override --help`, or `gjc ultragoal quality-gate --help` for command-specific requirements.",
|
|
5012
4768
|
"",
|
|
5013
4769
|
].join("\n");
|
|
5014
4770
|
}
|
|
@@ -5435,39 +5191,6 @@ async function dispatchUltragoalCommand(args: string[], cwd: string): Promise<Ul
|
|
|
5435
5191
|
.join("\n")}\n`,
|
|
5436
5192
|
};
|
|
5437
5193
|
}
|
|
5438
|
-
case "review-source": {
|
|
5439
|
-
const positional = args.filter(arg => !arg.startsWith("-"));
|
|
5440
|
-
const subcommand = positional[1];
|
|
5441
|
-
if (subcommand === "freeze") {
|
|
5442
|
-
const cohort = await freezeUltragoalReviewCohort({ cwd });
|
|
5443
|
-
return { status: 0, stdout: `${JSON.stringify(cohort, null, 2)}\n` };
|
|
5444
|
-
}
|
|
5445
|
-
if (subcommand === "dispatch") {
|
|
5446
|
-
const cohortId = flagValue(args, "--cohort-id") ?? "";
|
|
5447
|
-
const taskId = flagValue(args, "--task-id") ?? "";
|
|
5448
|
-
const lane = flagValue(args, "--lane") as ReviewSourceLane | undefined;
|
|
5449
|
-
const rerunCommand = flagValue(args, "--rerun-command") ?? "";
|
|
5450
|
-
if (
|
|
5451
|
-
!cohortId ||
|
|
5452
|
-
!taskId ||
|
|
5453
|
-
!lane ||
|
|
5454
|
-
!["cleaner", "architect", "qa", "critic"].includes(lane) ||
|
|
5455
|
-
!rerunCommand
|
|
5456
|
-
) {
|
|
5457
|
-
return {
|
|
5458
|
-
status: 1,
|
|
5459
|
-
stderr:
|
|
5460
|
-
"review-source dispatch requires --cohort-id, --task-id, --lane cleaner|architect|qa|critic, and --rerun-command\n",
|
|
5461
|
-
};
|
|
5462
|
-
}
|
|
5463
|
-
const dispatch = await dispatchUltragoalReviewLane({ cwd, cohortId, taskId, lane, rerunCommand });
|
|
5464
|
-
return { status: 0, stdout: `${JSON.stringify(dispatch, null, 2)}\n` };
|
|
5465
|
-
}
|
|
5466
|
-
return {
|
|
5467
|
-
status: 1,
|
|
5468
|
-
stderr: `Unknown gjc ultragoal review-source subcommand: ${subcommand ?? "(missing)"}\n`,
|
|
5469
|
-
};
|
|
5470
|
-
}
|
|
5471
5194
|
case "review": {
|
|
5472
5195
|
const result = await runUltragoalReview(cwd, args);
|
|
5473
5196
|
return {
|
|
@@ -1745,40 +1745,6 @@
|
|
|
1745
1745
|
"name": "goal-id",
|
|
1746
1746
|
"type": "string"
|
|
1747
1747
|
},
|
|
1748
|
-
{
|
|
1749
|
-
"appliesToVerbs": [
|
|
1750
|
-
"review-source"
|
|
1751
|
-
],
|
|
1752
|
-
"name": "cohort-id",
|
|
1753
|
-
"type": "string"
|
|
1754
|
-
},
|
|
1755
|
-
{
|
|
1756
|
-
"appliesToVerbs": [
|
|
1757
|
-
"review-source"
|
|
1758
|
-
],
|
|
1759
|
-
"name": "task-id",
|
|
1760
|
-
"type": "string"
|
|
1761
|
-
},
|
|
1762
|
-
{
|
|
1763
|
-
"appliesToVerbs": [
|
|
1764
|
-
"review-source"
|
|
1765
|
-
],
|
|
1766
|
-
"enumValues": [
|
|
1767
|
-
"cleaner",
|
|
1768
|
-
"architect",
|
|
1769
|
-
"qa",
|
|
1770
|
-
"critic"
|
|
1771
|
-
],
|
|
1772
|
-
"name": "lane",
|
|
1773
|
-
"type": "enum"
|
|
1774
|
-
},
|
|
1775
|
-
{
|
|
1776
|
-
"appliesToVerbs": [
|
|
1777
|
-
"review-source"
|
|
1778
|
-
],
|
|
1779
|
-
"name": "rerun-command",
|
|
1780
|
-
"type": "string"
|
|
1781
|
-
},
|
|
1782
1748
|
{
|
|
1783
1749
|
"appliesToVerbs": [
|
|
1784
1750
|
"steer"
|
|
@@ -1907,8 +1873,7 @@
|
|
|
1907
1873
|
"steer",
|
|
1908
1874
|
"classify-blocker",
|
|
1909
1875
|
"record-critic-verdict",
|
|
1910
|
-
"record-critic-gate-override"
|
|
1911
|
-
"review-source"
|
|
1876
|
+
"record-critic-gate-override"
|
|
1912
1877
|
],
|
|
1913
1878
|
"name": "json",
|
|
1914
1879
|
"type": "boolean"
|
|
@@ -2005,10 +1970,6 @@
|
|
|
2005
1970
|
"name": "quality-gate",
|
|
2006
1971
|
"surface": "command-positional"
|
|
2007
1972
|
},
|
|
2008
|
-
{
|
|
2009
|
-
"name": "review-source",
|
|
2010
|
-
"surface": "command-positional"
|
|
2011
|
-
},
|
|
2012
1973
|
{
|
|
2013
1974
|
"name": "graph",
|
|
2014
1975
|
"planned": true,
|
|
@@ -306,7 +306,6 @@ export const WORKFLOW_MANIFEST: Record<CanonicalGjcWorkflowSkill, SkillManifest>
|
|
|
306
306
|
"record-critic-verdict",
|
|
307
307
|
"record-critic-gate-override",
|
|
308
308
|
"quality-gate",
|
|
309
|
-
"review-source",
|
|
310
309
|
]),
|
|
311
310
|
...plannedVerbs(PLANNED_ADMIN_VERBS),
|
|
312
311
|
],
|
|
@@ -363,16 +362,6 @@ export const WORKFLOW_MANIFEST: Record<CanonicalGjcWorkflowSkill, SkillManifest>
|
|
|
363
362
|
{ name: "classification-event-id", type: "string", appliesToVerbs: ["record-critic-verdict"] },
|
|
364
363
|
{ name: "quality-gate-json", type: "string", appliesToVerbs: ["checkpoint", "quality-gate"] },
|
|
365
364
|
{ name: "goal-id", type: "string", appliesToVerbs: ["quality-gate"] },
|
|
366
|
-
{ name: "cohort-id", type: "string", appliesToVerbs: ["review-source"] },
|
|
367
|
-
{ name: "task-id", type: "string", appliesToVerbs: ["review-source"] },
|
|
368
|
-
{
|
|
369
|
-
name: "lane",
|
|
370
|
-
type: "enum",
|
|
371
|
-
enumValues: ["cleaner", "architect", "qa", "critic"],
|
|
372
|
-
appliesToVerbs: ["review-source"],
|
|
373
|
-
},
|
|
374
|
-
{ name: "rerun-command", type: "string", appliesToVerbs: ["review-source"] },
|
|
375
|
-
|
|
376
365
|
{ name: "goal-id", type: "string", appliesToVerbs: ["steer"] },
|
|
377
366
|
{ name: "goal-id", type: "string", appliesToVerbs: ["classify-blocker"] },
|
|
378
367
|
{
|
|
@@ -424,7 +413,6 @@ export const WORKFLOW_MANIFEST: Record<CanonicalGjcWorkflowSkill, SkillManifest>
|
|
|
424
413
|
"classify-blocker",
|
|
425
414
|
"record-critic-verdict",
|
|
426
415
|
"record-critic-gate-override",
|
|
427
|
-
"review-source",
|
|
428
416
|
],
|
|
429
417
|
},
|
|
430
418
|
{ name: "directive-json", type: "string", appliesToVerbs: ["steer"], planned: true },
|
|
@@ -11,16 +11,11 @@
|
|
|
11
11
|
* - agent://<id>?q=<query> - JSON extraction via query form
|
|
12
12
|
*/
|
|
13
13
|
import { createHash } from "node:crypto";
|
|
14
|
+
import * as fs from "node:fs/promises";
|
|
14
15
|
import * as path from "node:path";
|
|
15
16
|
import { isEnoent } from "@gajae-code/utils";
|
|
16
17
|
import { applyQuery, pathToQuery } from "./json-query";
|
|
17
|
-
import {
|
|
18
|
-
type AuthorizedArtifactsRoot,
|
|
19
|
-
authorizedArtifactsDirsFromContext,
|
|
20
|
-
closeAuthorizedArtifactsRoot,
|
|
21
|
-
openAuthorizedArtifactsRoot,
|
|
22
|
-
readAuthorizedArtifactFile,
|
|
23
|
-
} from "./registry-helpers";
|
|
18
|
+
import { authorizedArtifactsDirsFromContext } from "./registry-helpers";
|
|
24
19
|
import type { InternalResource, InternalUrl, ProtocolHandler, ResolveContext } from "./types";
|
|
25
20
|
|
|
26
21
|
interface AgentOutputMetadata {
|
|
@@ -41,24 +36,6 @@ interface ManagedOutputSelector {
|
|
|
41
36
|
metadataSha256: string;
|
|
42
37
|
}
|
|
43
38
|
|
|
44
|
-
const MAX_AGENT_SELECTOR_BYTES = 64 * 1024;
|
|
45
|
-
const MAX_AGENT_METADATA_BYTES = 1024 * 1024;
|
|
46
|
-
const MAX_AGENT_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
47
|
-
|
|
48
|
-
function readRetainedAgentFile(
|
|
49
|
-
outputId: string,
|
|
50
|
-
root: AuthorizedArtifactsRoot,
|
|
51
|
-
filename: string,
|
|
52
|
-
maxBytes: number,
|
|
53
|
-
): Buffer | null {
|
|
54
|
-
const captured = readAuthorizedArtifactFile(root, filename, maxBytes);
|
|
55
|
-
if (!captured) return null;
|
|
56
|
-
if (captured.size !== captured.bytes.byteLength) {
|
|
57
|
-
throw new Error(`agent://${outputId} retained file exceeds read capacity`);
|
|
58
|
-
}
|
|
59
|
-
return captured.bytes;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
39
|
function isSafeGenerationFilename(filename: unknown): filename is string {
|
|
63
40
|
return typeof filename === "string" && /^[a-zA-Z0-9_.-]+$/.test(filename);
|
|
64
41
|
}
|
|
@@ -79,14 +56,17 @@ function isManagedOutputSelector(value: unknown, outputId: string): value is Man
|
|
|
79
56
|
);
|
|
80
57
|
}
|
|
81
58
|
|
|
82
|
-
function readManagedOutputSelector(
|
|
59
|
+
async function readManagedOutputSelector(
|
|
83
60
|
outputId: string,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
61
|
+
selectorPath: string,
|
|
62
|
+
): Promise<ManagedOutputSelector | null> {
|
|
63
|
+
let raw: string;
|
|
64
|
+
try {
|
|
65
|
+
raw = await Bun.file(selectorPath).text();
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (isEnoent(error)) return null;
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
90
70
|
let parsed: unknown;
|
|
91
71
|
try {
|
|
92
72
|
parsed = JSON.parse(raw);
|
|
@@ -96,18 +76,6 @@ function readManagedOutputSelector(
|
|
|
96
76
|
if (!isManagedOutputSelector(parsed, outputId)) throw new Error(`agent://${outputId} malformed output selector`);
|
|
97
77
|
return parsed;
|
|
98
78
|
}
|
|
99
|
-
function sameManagedOutputSelector(left: ManagedOutputSelector, right: ManagedOutputSelector): boolean {
|
|
100
|
-
return (
|
|
101
|
-
left.outputFilename === right.outputFilename &&
|
|
102
|
-
left.metadataFilename === right.metadataFilename &&
|
|
103
|
-
left.outputSizeBytes === right.outputSizeBytes &&
|
|
104
|
-
left.outputSha256 === right.outputSha256 &&
|
|
105
|
-
left.metadataSizeBytes === right.metadataSizeBytes &&
|
|
106
|
-
left.metadataSha256 === right.metadataSha256
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const MANAGED_SELECTOR_READ_ATTEMPTS = 8;
|
|
111
79
|
|
|
112
80
|
function isAgentOutputMetadata(value: unknown, outputId: string): value is AgentOutputMetadata {
|
|
113
81
|
if (!value || typeof value !== "object") return false;
|
|
@@ -124,11 +92,18 @@ function isAgentOutputMetadata(value: unknown, outputId: string): value is Agent
|
|
|
124
92
|
|
|
125
93
|
async function verifyAgentOutputMetadata(
|
|
126
94
|
outputId: string,
|
|
127
|
-
|
|
95
|
+
foundPath: string,
|
|
96
|
+
metadataPath: string,
|
|
128
97
|
bytes: Buffer,
|
|
129
98
|
selector?: ManagedOutputSelector,
|
|
130
99
|
): Promise<void> {
|
|
131
|
-
|
|
100
|
+
let metaRaw: string;
|
|
101
|
+
try {
|
|
102
|
+
metaRaw = await Bun.file(metadataPath).text();
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (isEnoent(err)) throw new Error(`agent://${outputId} missing metadata`);
|
|
105
|
+
throw err;
|
|
106
|
+
}
|
|
132
107
|
let parsed: unknown;
|
|
133
108
|
try {
|
|
134
109
|
parsed = JSON.parse(metaRaw);
|
|
@@ -138,7 +113,9 @@ async function verifyAgentOutputMetadata(
|
|
|
138
113
|
if (!isAgentOutputMetadata(parsed, outputId)) {
|
|
139
114
|
throw new Error(`agent://${outputId} malformed metadata`);
|
|
140
115
|
}
|
|
141
|
-
|
|
116
|
+
const metadataBytes = Buffer.from(metaRaw, "utf8");
|
|
117
|
+
const stat = await fs.stat(foundPath);
|
|
118
|
+
if (stat.size !== parsed.sizeBytes || bytes.byteLength !== parsed.sizeBytes) {
|
|
142
119
|
throw new Error(`agent://${outputId} size mismatch`);
|
|
143
120
|
}
|
|
144
121
|
const sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
@@ -195,64 +172,27 @@ export class AgentProtocolHandler implements ProtocolHandler {
|
|
|
195
172
|
let foundPath: string | undefined;
|
|
196
173
|
let foundMetadataPath: string | undefined;
|
|
197
174
|
let foundSelector: ManagedOutputSelector | undefined;
|
|
198
|
-
let foundBytes: Buffer | undefined;
|
|
199
|
-
let foundMetadataBytes: Buffer | undefined;
|
|
200
175
|
let anyDirExists = false;
|
|
201
176
|
|
|
202
177
|
for (const dir of dirs) {
|
|
203
|
-
let root: AuthorizedArtifactsRoot;
|
|
204
178
|
try {
|
|
205
|
-
|
|
179
|
+
await fs.stat(dir);
|
|
206
180
|
anyDirExists = true;
|
|
207
181
|
} catch (err) {
|
|
208
182
|
if (isEnoent(err)) continue;
|
|
209
183
|
throw err;
|
|
210
184
|
}
|
|
185
|
+
const selector = await readManagedOutputSelector(outputId, path.join(dir, `${outputId}.md.selector.json`));
|
|
186
|
+
const candidate = selector ? path.join(dir, selector.outputFilename) : path.join(dir, `${outputId}.md`);
|
|
187
|
+
const metadataPath = selector ? path.join(dir, selector.metadataFilename) : `${candidate}.meta.json`;
|
|
211
188
|
try {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (rawBuffer && metadataBuffer) {
|
|
220
|
-
if (selector) {
|
|
221
|
-
const refreshed = readManagedOutputSelector(outputId, root, selectorFilename);
|
|
222
|
-
if (!refreshed || !sameManagedOutputSelector(selector, refreshed)) {
|
|
223
|
-
if (attempt + 1 >= MANAGED_SELECTOR_READ_ATTEMPTS) {
|
|
224
|
-
throw new Error(`agent://${outputId} generation changed during read`);
|
|
225
|
-
}
|
|
226
|
-
if (!refreshed) throw new Error(`agent://${outputId} generation changed during read`);
|
|
227
|
-
selector = refreshed;
|
|
228
|
-
continue;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
if (foundPath) throw new Error(`agent://${outputId} ambiguous id in authorized artifacts`);
|
|
232
|
-
foundPath = path.join(dir, candidateFilename);
|
|
233
|
-
foundMetadataPath = path.join(dir, metadataFilename);
|
|
234
|
-
foundSelector = selector ?? undefined;
|
|
235
|
-
foundBytes = rawBuffer;
|
|
236
|
-
foundMetadataBytes = metadataBuffer;
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
if (!selector) {
|
|
240
|
-
if (!rawBuffer) break;
|
|
241
|
-
throw new Error(`agent://${outputId} missing metadata`);
|
|
242
|
-
}
|
|
243
|
-
if (attempt + 1 >= MANAGED_SELECTOR_READ_ATTEMPTS) {
|
|
244
|
-
throw new Error(`agent://${outputId} generation changed during read`);
|
|
245
|
-
}
|
|
246
|
-
const refreshed = readManagedOutputSelector(outputId, root, selectorFilename);
|
|
247
|
-
if (!refreshed) throw new Error(`agent://${outputId} generation changed during read`);
|
|
248
|
-
if (sameManagedOutputSelector(selector, refreshed)) {
|
|
249
|
-
if (!rawBuffer) throw new Error(`agent://${outputId} selected generation unavailable`);
|
|
250
|
-
throw new Error(`agent://${outputId} missing metadata`);
|
|
251
|
-
}
|
|
252
|
-
selector = refreshed;
|
|
253
|
-
}
|
|
254
|
-
} finally {
|
|
255
|
-
closeAuthorizedArtifactsRoot(root);
|
|
189
|
+
await fs.stat(candidate);
|
|
190
|
+
if (foundPath) throw new Error(`agent://${outputId} ambiguous id in authorized artifacts`);
|
|
191
|
+
foundPath = candidate;
|
|
192
|
+
foundMetadataPath = metadataPath;
|
|
193
|
+
foundSelector = selector ?? undefined;
|
|
194
|
+
} catch (err) {
|
|
195
|
+
if (!isEnoent(err)) throw err;
|
|
256
196
|
}
|
|
257
197
|
}
|
|
258
198
|
|
|
@@ -260,12 +200,12 @@ export class AgentProtocolHandler implements ProtocolHandler {
|
|
|
260
200
|
throw new Error("No artifacts directory found");
|
|
261
201
|
}
|
|
262
202
|
|
|
263
|
-
if (!foundPath
|
|
203
|
+
if (!foundPath) {
|
|
264
204
|
throw new Error(`agent://${outputId} not found`);
|
|
265
205
|
}
|
|
266
206
|
|
|
267
|
-
const rawBytes =
|
|
268
|
-
await verifyAgentOutputMetadata(outputId,
|
|
207
|
+
const rawBytes = Buffer.from(await Bun.file(foundPath).arrayBuffer());
|
|
208
|
+
await verifyAgentOutputMetadata(outputId, foundPath, foundMetadataPath!, rawBytes, foundSelector);
|
|
269
209
|
const rawContent = rawBytes.toString("utf8");
|
|
270
210
|
const notes: string[] = [];
|
|
271
211
|
let content = rawContent;
|