@open-agent-toolkit/cli 0.2.24 → 0.2.26
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/assets/agents/oat-reviewer.md +12 -1
- package/assets/docs/cli-utilities/configuration.md +44 -15
- package/assets/docs/reference/cli-reference.md +1 -1
- package/assets/docs/workflows/projects/dispatch-ceiling.md +67 -16
- package/assets/docs/workflows/projects/reviews.md +19 -10
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +4 -4
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +4 -4
- package/assets/skills/oat-project-implement/SKILL.md +9 -11
- package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +18 -9
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +4 -4
- package/assets/skills/oat-project-implement/references/phase-execution.md +42 -4
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +4 -4
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +4 -4
- package/assets/skills/oat-project-review-provide/SKILL.md +124 -26
- package/assets/skills/oat-project-review-provide-remote/SKILL.md +64 -15
- package/assets/skills/oat-project-review-receive/SKILL.md +21 -1
- package/assets/skills/oat-project-review-receive-remote/SKILL.md +39 -2
- package/assets/skills/oat-review-provide-remote/SKILL.md +55 -12
- package/assets/templates/plan.md +13 -7
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +35 -11
- package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
- package/dist/commands/project/dispatch-ceiling/index.js +90 -0
- package/dist/commands/review/latest.d.ts.map +1 -1
- package/dist/commands/review/latest.js +22 -9
- package/dist/config/dispatch-notices.d.ts +8 -0
- package/dist/config/dispatch-notices.d.ts.map +1 -0
- package/dist/config/dispatch-notices.js +79 -0
- package/dist/config/dispatch-policy-options.d.ts +2 -0
- package/dist/config/dispatch-policy-options.d.ts.map +1 -1
- package/dist/config/dispatch-policy-options.js +14 -2
- package/dist/config/resolve.js +1 -1
- package/dist/providers/identity/dispatch-report.d.ts +17 -0
- package/dist/providers/identity/dispatch-report.d.ts.map +1 -1
- package/dist/providers/identity/dispatch-report.js +30 -0
- package/dist/review-remote/body-builder.d.ts +12 -3
- package/dist/review-remote/body-builder.d.ts.map +1 -1
- package/dist/review-remote/body-builder.js +11 -0
- package/dist/review-remote/marker-parser.d.ts +8 -3
- package/dist/review-remote/marker-parser.d.ts.map +1 -1
- package/dist/review-remote/marker-parser.js +13 -2
- package/dist/review-remote/narrowing.d.ts +44 -8
- package/dist/review-remote/narrowing.d.ts.map +1 -1
- package/dist/review-remote/narrowing.js +106 -10
- package/package.json +2 -2
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
/** Token that opens the marker comment block. */
|
|
15
15
|
export declare const MARKER_BLOCK_OPEN = "oat-review-metadata";
|
|
16
|
-
export type ReviewInvocation = 'manual' | 'auto';
|
|
16
|
+
export type ReviewInvocation = 'manual' | 'auto' | 'gate';
|
|
17
17
|
export interface MarkerBlock {
|
|
18
18
|
/** Always `true` for an OAT provide-remote review; the discriminator. */
|
|
19
19
|
oat_provide_remote: true;
|
|
@@ -26,8 +26,13 @@ export interface MarkerBlock {
|
|
|
26
26
|
* `null` value) discriminates project rail from ad-hoc rail.
|
|
27
27
|
*/
|
|
28
28
|
oat_project?: string;
|
|
29
|
-
/**
|
|
30
|
-
|
|
29
|
+
/**
|
|
30
|
+
* How the review was invoked. Missing and unknown values stay undefined so
|
|
31
|
+
* legacy markers cannot silently acquire lifecycle lineage.
|
|
32
|
+
*/
|
|
33
|
+
oat_review_invocation?: ReviewInvocation;
|
|
34
|
+
/** Exact gate target. Required before a gate marker can establish lineage. */
|
|
35
|
+
oat_gate_target?: string;
|
|
31
36
|
/** Forward-compat bag for unknown marker keys. Omitted when none seen. */
|
|
32
37
|
extras?: Record<string, string>;
|
|
33
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marker-parser.d.ts","sourceRoot":"","sources":["../../src/review-remote/marker-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AAavD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"marker-parser.d.ts","sourceRoot":"","sources":["../../src/review-remote/marker-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AAavD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,WAAW;IAC1B,yEAAyE;IACzE,kBAAkB,EAAE,IAAI,CAAC;IACzB,oDAAoD;IACpD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,gBAAgB,CAAC;IACzC,8EAA8E;IAC9E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAWD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CA+EjE"}
|
|
@@ -26,6 +26,7 @@ const KNOWN_KEYS = new Set([
|
|
|
26
26
|
'oat_review_scope',
|
|
27
27
|
'oat_project',
|
|
28
28
|
'oat_review_invocation',
|
|
29
|
+
'oat_gate_target',
|
|
29
30
|
]);
|
|
30
31
|
/**
|
|
31
32
|
* Parse the first OAT marker block out of a posted-review body.
|
|
@@ -73,17 +74,27 @@ export function parseMarkerBlock(body) {
|
|
|
73
74
|
return null;
|
|
74
75
|
}
|
|
75
76
|
const invocationRaw = raw['oat_review_invocation'];
|
|
76
|
-
const oat_review_invocation = invocationRaw === '
|
|
77
|
+
const oat_review_invocation = invocationRaw === 'manual' ||
|
|
78
|
+
invocationRaw === 'auto' ||
|
|
79
|
+
invocationRaw === 'gate'
|
|
80
|
+
? invocationRaw
|
|
81
|
+
: undefined;
|
|
77
82
|
const block = {
|
|
78
83
|
oat_provide_remote: true,
|
|
79
84
|
oat_review_head_sha: headSha,
|
|
80
85
|
oat_review_scope: scope,
|
|
81
|
-
oat_review_invocation,
|
|
82
86
|
};
|
|
87
|
+
if (oat_review_invocation !== undefined) {
|
|
88
|
+
block.oat_review_invocation = oat_review_invocation;
|
|
89
|
+
}
|
|
83
90
|
// Project key existence (not a null value) discriminates the rail.
|
|
84
91
|
if ('oat_project' in raw && raw['oat_project'] !== '') {
|
|
85
92
|
block.oat_project = raw['oat_project'];
|
|
86
93
|
}
|
|
94
|
+
const gateTarget = raw['oat_gate_target']?.trim();
|
|
95
|
+
if (gateTarget) {
|
|
96
|
+
block.oat_gate_target = gateTarget;
|
|
97
|
+
}
|
|
87
98
|
const extras = {};
|
|
88
99
|
for (const [key, value] of Object.entries(raw)) {
|
|
89
100
|
if (!KNOWN_KEYS.has(key)) {
|
|
@@ -9,8 +9,15 @@
|
|
|
9
9
|
* range, so a rebased/force-pushed/shallow prior SHA never produces a
|
|
10
10
|
* misleading partial range.
|
|
11
11
|
*/
|
|
12
|
-
import type { ReviewInvocation } from './marker-parser.js';
|
|
12
|
+
import type { MarkerBlock, ReviewInvocation } from './marker-parser.js';
|
|
13
13
|
export type ReviewRail = 'ad-hoc' | 'project';
|
|
14
|
+
export type ReviewLineage = {
|
|
15
|
+
kind: 'lifecycle';
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'gate';
|
|
18
|
+
target: string;
|
|
19
|
+
};
|
|
20
|
+
export type ReviewInvocationKind = ReviewInvocation;
|
|
14
21
|
/** A prior provide-remote review, distilled from its parsed marker block. */
|
|
15
22
|
export interface PriorReview {
|
|
16
23
|
/** Full 40-char SHA recorded by the prior review. */
|
|
@@ -19,10 +26,34 @@ export interface PriorReview {
|
|
|
19
26
|
scope: string;
|
|
20
27
|
/** Project path — present only for project-rail reviews. */
|
|
21
28
|
project?: string;
|
|
22
|
-
invocation
|
|
29
|
+
invocation?: ReviewInvocationKind;
|
|
30
|
+
/** Review lineage; absent on legacy records, which are not eligible. */
|
|
31
|
+
lineage?: ReviewLineage;
|
|
23
32
|
/** ISO-8601 review submission timestamp, used for descending sort. */
|
|
24
33
|
submittedAt: string;
|
|
25
34
|
}
|
|
35
|
+
/** Provenance persisted on one tracked Reviews ledger row. */
|
|
36
|
+
export interface ReviewLedgerProvenance {
|
|
37
|
+
reviewedHead: string;
|
|
38
|
+
scope: string;
|
|
39
|
+
project?: string;
|
|
40
|
+
invocation?: string;
|
|
41
|
+
gateTarget?: string;
|
|
42
|
+
artifact: string;
|
|
43
|
+
submittedAt: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Normalize one parsed GitHub marker into a narrowing candidate. Unknown
|
|
47
|
+
* invocation and target-less gate markers preserve their metadata but carry no
|
|
48
|
+
* lineage, so tuple matching rejects them and fails open.
|
|
49
|
+
*/
|
|
50
|
+
export declare function priorReviewFromMarker(marker: MarkerBlock, submittedAt: string): PriorReview;
|
|
51
|
+
/**
|
|
52
|
+
* Normalize durable ledger provenance into the same candidate shape used by
|
|
53
|
+
* artifact-sourced reviews. Both sources then pass through {@link matchesTuple}
|
|
54
|
+
* and cannot drift into separate lineage predicates.
|
|
55
|
+
*/
|
|
56
|
+
export declare function priorReviewFromLedger(row: ReviewLedgerProvenance): PriorReview;
|
|
26
57
|
/**
|
|
27
58
|
* Narrow git surface needed by the guard. Callers pass a worktree-bound
|
|
28
59
|
* implementation (rich-context mode) or a fetch-capable one (diff-only mode);
|
|
@@ -35,6 +66,8 @@ export interface GitInvoker {
|
|
|
35
66
|
isAncestor(sha: string, head: string): Promise<boolean>;
|
|
36
67
|
/** `git fetch origin <sha>:<ref>` — fetch a single ref (diff-only mode). */
|
|
37
68
|
fetchRef(sha: string): Promise<boolean>;
|
|
69
|
+
/** Files changed in `<priorSha>..<headSha>`. */
|
|
70
|
+
changedFiles(priorSha: string, headSha: string): Promise<string[]>;
|
|
38
71
|
}
|
|
39
72
|
export interface NarrowingInput {
|
|
40
73
|
reviews: PriorReview[];
|
|
@@ -43,30 +76,33 @@ export interface NarrowingInput {
|
|
|
43
76
|
project: string | null;
|
|
44
77
|
/** Current scope token, or `ad-hoc`. */
|
|
45
78
|
scope: string;
|
|
79
|
+
/** Current review lineage, including the target for gate invocations. */
|
|
80
|
+
lineage: ReviewLineage;
|
|
46
81
|
/** Current PR HEAD SHA. */
|
|
47
82
|
headSha: string;
|
|
48
83
|
git: GitInvoker;
|
|
49
84
|
/** `--narrow` was passed explicitly: guard failure becomes a hard error. */
|
|
50
85
|
forceNarrow?: boolean;
|
|
51
|
-
/**
|
|
52
|
-
|
|
86
|
+
/** Resolved preference: unset and true narrow; false forces full scope. */
|
|
87
|
+
narrowingPreference?: boolean;
|
|
53
88
|
/** Diff-only mode (no ephemeral worktree): fetch the single ref first. */
|
|
54
89
|
diffOnly?: boolean;
|
|
55
90
|
}
|
|
56
|
-
export type FullScopeReason = 'no-prior-review' | 'stale-sha';
|
|
91
|
+
export type FullScopeReason = 'narrowing-disabled' | 'no-prior-review' | 'stale-sha';
|
|
92
|
+
export type RangeClassification = 'empty' | 'bookkeeping-only' | 'substantive';
|
|
57
93
|
export interface NarrowRangeResult {
|
|
58
94
|
kind: 'narrow-range';
|
|
59
95
|
priorSha: string;
|
|
60
96
|
headSha: string;
|
|
61
|
-
|
|
62
|
-
|
|
97
|
+
classification: RangeClassification;
|
|
98
|
+
/** Why classification conservatively fell back, when enumeration failed. */
|
|
99
|
+
classificationReason?: 'changed-files-unavailable';
|
|
63
100
|
}
|
|
64
101
|
export interface FullScopeFallbackResult {
|
|
65
102
|
kind: 'full-scope-fallback';
|
|
66
103
|
reason: FullScopeReason;
|
|
67
104
|
/** The stale SHA that triggered the fallback, when applicable. */
|
|
68
105
|
priorSha?: string;
|
|
69
|
-
prompted: boolean;
|
|
70
106
|
}
|
|
71
107
|
export interface HardErrorResult {
|
|
72
108
|
kind: 'hard-error';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"narrowing.d.ts","sourceRoot":"","sources":["../../src/review-remote/narrowing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"narrowing.d.ts","sourceRoot":"","sources":["../../src/review-remote/narrowing.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,16 +9,85 @@
|
|
|
9
9
|
* range, so a rebased/force-pushed/shallow prior SHA never produces a
|
|
10
10
|
* misleading partial range.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
const FULL_COMMIT_SHA = /^[0-9a-f]{40}$/i;
|
|
13
|
+
/**
|
|
14
|
+
* Normalize one parsed GitHub marker into a narrowing candidate. Unknown
|
|
15
|
+
* invocation and target-less gate markers preserve their metadata but carry no
|
|
16
|
+
* lineage, so tuple matching rejects them and fails open.
|
|
17
|
+
*/
|
|
18
|
+
export function priorReviewFromMarker(marker, submittedAt) {
|
|
19
|
+
const lineage = markerLineage(marker);
|
|
20
|
+
return {
|
|
21
|
+
headSha: marker.oat_review_head_sha,
|
|
22
|
+
scope: marker.oat_review_scope,
|
|
23
|
+
...(marker.oat_project ? { project: marker.oat_project } : {}),
|
|
24
|
+
...(marker.oat_review_invocation
|
|
25
|
+
? { invocation: marker.oat_review_invocation }
|
|
26
|
+
: {}),
|
|
27
|
+
...(lineage ? { lineage } : {}),
|
|
28
|
+
submittedAt,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function markerLineage(marker) {
|
|
32
|
+
if (marker.oat_review_invocation === 'manual' ||
|
|
33
|
+
marker.oat_review_invocation === 'auto') {
|
|
34
|
+
return { kind: 'lifecycle' };
|
|
35
|
+
}
|
|
36
|
+
const gateTarget = marker.oat_gate_target?.trim();
|
|
37
|
+
return marker.oat_review_invocation === 'gate' && gateTarget
|
|
38
|
+
? { kind: 'gate', target: gateTarget }
|
|
39
|
+
: undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Normalize durable ledger provenance into the same candidate shape used by
|
|
43
|
+
* artifact-sourced reviews. Both sources then pass through {@link matchesTuple}
|
|
44
|
+
* and cannot drift into separate lineage predicates.
|
|
45
|
+
*/
|
|
46
|
+
export function priorReviewFromLedger(row) {
|
|
47
|
+
const invocation = isReviewInvocationKind(row.invocation)
|
|
48
|
+
? row.invocation
|
|
49
|
+
: undefined;
|
|
50
|
+
const lineage = ledgerLineage(row);
|
|
51
|
+
return {
|
|
52
|
+
headSha: row.reviewedHead,
|
|
53
|
+
scope: row.scope,
|
|
54
|
+
...(row.project ? { project: row.project } : {}),
|
|
55
|
+
...(invocation ? { invocation } : {}),
|
|
56
|
+
...(lineage ? { lineage } : {}),
|
|
57
|
+
submittedAt: row.submittedAt,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function isReviewInvocationKind(invocation) {
|
|
61
|
+
return (invocation === 'manual' || invocation === 'auto' || invocation === 'gate');
|
|
62
|
+
}
|
|
63
|
+
function ledgerLineage(row) {
|
|
64
|
+
if (row.invocation === 'manual' || row.invocation === 'auto') {
|
|
65
|
+
return { kind: 'lifecycle' };
|
|
66
|
+
}
|
|
67
|
+
const gateTarget = row.gateTarget?.trim();
|
|
68
|
+
return row.invocation === 'gate' && gateTarget
|
|
69
|
+
? { kind: 'gate', target: gateTarget }
|
|
70
|
+
: undefined;
|
|
71
|
+
}
|
|
72
|
+
/** Does a prior review match the current rail, project, scope, and lineage? */
|
|
13
73
|
function matchesTuple(review, input) {
|
|
74
|
+
if (!FULL_COMMIT_SHA.test(review.headSha)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
const sameLineage = review.lineage !== undefined &&
|
|
78
|
+
review.lineage.kind === input.lineage.kind &&
|
|
79
|
+
(input.lineage.kind === 'lifecycle' ||
|
|
80
|
+
(review.lineage.kind === 'gate' &&
|
|
81
|
+
review.lineage.target === input.lineage.target));
|
|
14
82
|
if (input.rail === 'ad-hoc') {
|
|
15
83
|
// Ad-hoc: scope sentinel must match AND there must be no project key.
|
|
16
|
-
return review.scope === 'ad-hoc' && review.project === undefined;
|
|
84
|
+
return (review.scope === 'ad-hoc' && review.project === undefined && sameLineage);
|
|
17
85
|
}
|
|
18
86
|
// Project rail: same project AND same scope.
|
|
19
87
|
return (review.project !== undefined &&
|
|
20
88
|
review.project === input.project &&
|
|
21
|
-
review.scope === input.scope
|
|
89
|
+
review.scope === input.scope &&
|
|
90
|
+
sameLineage);
|
|
22
91
|
}
|
|
23
92
|
function mostRecentMatch(input) {
|
|
24
93
|
const matches = input.reviews.filter((r) => matchesTuple(r, input));
|
|
@@ -48,16 +117,47 @@ async function guardPasses(priorSha, input) {
|
|
|
48
117
|
}
|
|
49
118
|
return input.git.isAncestor(priorSha, input.headSha);
|
|
50
119
|
}
|
|
120
|
+
async function classifyRange(priorSha, input) {
|
|
121
|
+
let files;
|
|
122
|
+
try {
|
|
123
|
+
files = await input.git.changedFiles(priorSha, input.headSha);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Classification is reporting-only, so enumeration failure must not block
|
|
127
|
+
// dispatch. Treat unknown work as substantive and disclose the fallback.
|
|
128
|
+
return {
|
|
129
|
+
classification: 'substantive',
|
|
130
|
+
classificationReason: 'changed-files-unavailable',
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (files.length === 0) {
|
|
134
|
+
return { classification: 'empty' };
|
|
135
|
+
}
|
|
136
|
+
const projectPrefix = input.project === null ? null : `${input.project.replace(/\/+$/, '')}/`;
|
|
137
|
+
// Path-only classification is sufficient while it is reporting-only. If it
|
|
138
|
+
// ever authorizes skipping work, strengthen it with lifecycle corroboration.
|
|
139
|
+
return {
|
|
140
|
+
classification: projectPrefix !== null &&
|
|
141
|
+
files.every((file) => file.startsWith(projectPrefix))
|
|
142
|
+
? 'bookkeeping-only'
|
|
143
|
+
: 'substantive',
|
|
144
|
+
};
|
|
145
|
+
}
|
|
51
146
|
/**
|
|
52
147
|
* Pick the narrowing target for the current re-review pass.
|
|
53
148
|
*/
|
|
54
149
|
export async function pickNarrowingTarget(input) {
|
|
150
|
+
if (input.narrowingPreference === false && !input.forceNarrow) {
|
|
151
|
+
return {
|
|
152
|
+
kind: 'full-scope-fallback',
|
|
153
|
+
reason: 'narrowing-disabled',
|
|
154
|
+
};
|
|
155
|
+
}
|
|
55
156
|
const prior = mostRecentMatch(input);
|
|
56
157
|
if (!prior) {
|
|
57
158
|
return {
|
|
58
159
|
kind: 'full-scope-fallback',
|
|
59
160
|
reason: 'no-prior-review',
|
|
60
|
-
prompted: false,
|
|
61
161
|
};
|
|
62
162
|
}
|
|
63
163
|
const passed = await guardPasses(prior.headSha, input);
|
|
@@ -66,8 +166,7 @@ export async function pickNarrowingTarget(input) {
|
|
|
66
166
|
kind: 'narrow-range',
|
|
67
167
|
priorSha: prior.headSha,
|
|
68
168
|
headSha: input.headSha,
|
|
69
|
-
|
|
70
|
-
prompted: !input.autoNarrow,
|
|
169
|
+
...(await classifyRange(prior.headSha, input)),
|
|
71
170
|
};
|
|
72
171
|
}
|
|
73
172
|
// Guard failed. The user explicitly asked to narrow → hard error.
|
|
@@ -78,13 +177,10 @@ export async function pickNarrowingTarget(input) {
|
|
|
78
177
|
priorSha: prior.headSha,
|
|
79
178
|
};
|
|
80
179
|
}
|
|
81
|
-
// Otherwise
|
|
82
|
-
// auto-fallback notice (no prompt); manual mode does not prompt here either —
|
|
83
|
-
// the fallback is informational.
|
|
180
|
+
// Otherwise fail open to full scope and preserve the guard failure reason.
|
|
84
181
|
return {
|
|
85
182
|
kind: 'full-scope-fallback',
|
|
86
183
|
reason: 'stale-sha',
|
|
87
184
|
priorSha: prior.headSha,
|
|
88
|
-
prompted: false,
|
|
89
185
|
};
|
|
90
186
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-agent-toolkit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.26",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Open Agent Toolkit CLI",
|
|
6
6
|
"homepage": "https://github.com/voxmedia/open-agent-toolkit/tree/main/packages/cli",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"ora": "^9.0.0",
|
|
35
35
|
"yaml": "2.8.2",
|
|
36
36
|
"zod": "^3.25.76",
|
|
37
|
-
"@open-agent-toolkit/control-plane": "0.2.
|
|
37
|
+
"@open-agent-toolkit/control-plane": "0.2.26"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.10.0",
|