@skrr-ai/cli 0.1.127 → 0.1.128
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/dist/base-command.d.ts +23 -0
- package/dist/base-command.js +72 -0
- package/dist/commands/agents/chat.js +7 -0
- package/dist/commands/commitments/action-history.js +1 -1
- package/dist/commands/commitments/cases.js +3 -3
- package/dist/commands/commitments/check-now.d.ts +10 -2
- package/dist/commands/commitments/check-now.js +53 -3
- package/dist/commands/commitments/create.d.ts +6 -14
- package/dist/commands/commitments/create.js +61 -6
- package/dist/commands/commitments/doctor.js +3 -1
- package/dist/commands/commitments/effective-policy.d.ts +11 -1
- package/dist/commands/commitments/effective-policy.js +84 -4
- package/dist/commands/commitments/hypotheses.js +12 -12
- package/dist/commands/commitments/next-tick.js +5 -0
- package/dist/commands/commitments/observations.js +18 -17
- package/dist/commands/commitments/pack/list.js +2 -1
- package/dist/commands/commitments/pack/plan.d.ts +6 -0
- package/dist/commands/commitments/pack/plan.js +19 -0
- package/dist/commands/commitments/preflight.d.ts +19 -0
- package/dist/commands/commitments/preflight.js +108 -10
- package/dist/commands/commitments/trigger-status.js +8 -0
- package/dist/commands/commitments/update.d.ts +1 -0
- package/dist/commands/commitments/update.js +45 -4
- package/dist/commands/commitments/wake-capabilities.js +13 -0
- package/dist/commands/compass/archive.js +4 -0
- package/dist/commands/compass/commitments.js +15 -1
- package/dist/commands/compass/create.js +11 -3
- package/dist/commands/compass/effective-policy.d.ts +1 -0
- package/dist/commands/compass/effective-policy.js +8 -7
- package/dist/commands/compass/revert.d.ts +8 -0
- package/dist/commands/compass/revert.js +25 -1
- package/dist/commands/compass/revisions.d.ts +2 -0
- package/dist/commands/compass/revisions.js +18 -2
- package/dist/commands/compass/tactic.d.ts +5 -3
- package/dist/commands/compass/tactic.js +8 -7
- package/dist/commands/compass/update.js +19 -3
- package/dist/commands/followups/act.js +1 -1
- package/dist/commands/followups/cancel.js +13 -0
- package/dist/commands/followups/observability.js +20 -1
- package/dist/commands/followups/plans/create.js +12 -1
- package/dist/commands/followups/show.js +13 -0
- package/dist/commands/followups/watch.js +1 -1
- package/dist/commands/goals/planner-config.d.ts +11 -0
- package/dist/commands/goals/planner-config.js +16 -0
- package/dist/commands/machines/update.d.ts +5 -3
- package/dist/commands/machines/update.js +70 -9
- package/dist/commands/northstars/create.js +3 -1
- package/dist/commands/northstars/governance/configure.d.ts +4 -0
- package/dist/commands/northstars/governance/configure.js +75 -9
- package/dist/commands/northstars/intents/decide.d.ts +6 -0
- package/dist/commands/northstars/intents/decide.js +69 -15
- package/dist/commands/northstars/intents/list.d.ts +2 -1
- package/dist/commands/northstars/intents/list.js +15 -5
- package/dist/commands/northstars/intents.d.ts +26 -0
- package/dist/commands/northstars/intents.js +49 -0
- package/dist/commands/northstars/list.d.ts +1 -0
- package/dist/commands/northstars/list.js +24 -3
- package/dist/commands/northstars/recommendations/list.d.ts +1 -1
- package/dist/commands/northstars/recommendations/list.js +3 -3
- package/dist/commands/northstars/recommendations.d.ts +19 -0
- package/dist/commands/northstars/recommendations.js +42 -0
- package/dist/commands/northstars/review-now.d.ts +1 -0
- package/dist/commands/northstars/review-now.js +10 -4
- package/dist/commands/northstars/reviews.d.ts +1 -0
- package/dist/commands/northstars/reviews.js +19 -3
- package/dist/commands/northstars/update.js +8 -1
- package/dist/commands/proposals/list.js +11 -2
- package/dist/commands/proposals/show.js +9 -1
- package/dist/commands/spaces/auto-executor/preview.d.ts +14 -1
- package/dist/commands/spaces/auto-executor/preview.js +35 -1
- package/dist/commands/spaces/auto-executor/run.d.ts +17 -0
- package/dist/commands/spaces/auto-executor/run.js +60 -2
- package/dist/commands/spaces/files/upload.js +1 -2
- package/dist/commands/spaces/index.js +23 -0
- package/dist/generated/agent-guides.json +69 -69
- package/dist/lib/agentic-stream.d.ts +9 -0
- package/dist/lib/agentic-stream.js +14 -0
- package/dist/lib/command-miss.js +11 -2
- package/dist/lib/commitment-attention-copy.d.ts +96 -0
- package/dist/lib/commitment-attention-copy.js +209 -0
- package/dist/lib/commitment-loop-status.d.ts +6 -0
- package/dist/lib/commitment-loop-status.js +16 -6
- package/dist/lib/commitments.d.ts +88 -0
- package/dist/lib/commitments.js +188 -0
- package/dist/lib/compass-body-help.d.ts +20 -0
- package/dist/lib/compass-body-help.js +61 -0
- package/dist/lib/compass-refusals.d.ts +35 -0
- package/dist/lib/compass-refusals.js +50 -0
- package/dist/lib/compasses.d.ts +80 -4
- package/dist/lib/compasses.js +232 -12
- package/dist/lib/followups.d.ts +22 -1
- package/dist/lib/followups.js +21 -1
- package/dist/lib/format.d.ts +11 -0
- package/dist/lib/format.js +29 -0
- package/dist/lib/initiatives.d.ts +9 -2
- package/dist/lib/initiatives.js +13 -2
- package/dist/lib/machines.d.ts +14 -1
- package/dist/lib/machines.js +24 -0
- package/dist/lib/northstars.d.ts +158 -24
- package/dist/lib/northstars.js +538 -26
- package/dist/lib/proposal-decision-command.js +21 -1
- package/dist/lib/proposals.d.ts +26 -1
- package/dist/lib/proposals.js +163 -0
- package/dist/lib/rate-limit-retry.d.ts +5 -0
- package/dist/lib/rate-limit-retry.js +19 -0
- package/dist/lib/roles.d.ts +1 -1
- package/dist/lib/space-charter.js +9 -0
- package/dist/lib/task-asset-upload.js +1 -2
- package/dist/lib/triggers.d.ts +1 -1
- package/dist/lib/triggers.js +11 -3
- package/dist/node_modules/@skrr-ai/data-provider/index.js +6504 -6493
- package/oclif.manifest.json +350 -34
- package/package.json +4 -4
|
@@ -82,7 +82,27 @@ class ProposalDecisionCommand extends base_command_1.BaseCommand {
|
|
|
82
82
|
}
|
|
83
83
|
this.handleApiError(error);
|
|
84
84
|
}
|
|
85
|
-
|
|
85
|
+
// A retry whose first response was lost lands on the decision that already took effect; the
|
|
86
|
+
// server marks it, so it is not worded like a first success (OSK-21191).
|
|
87
|
+
if (flags.json) {
|
|
88
|
+
this.log(JSON.stringify(result, null, 2));
|
|
89
|
+
}
|
|
90
|
+
else if (result.replayed) {
|
|
91
|
+
// The receipt repeats the decision that was ASKED for; what the intent's outcome actually is
|
|
92
|
+
// (applied, still waiting to be applied, rejected) is on the proposal, so read it (OSK-21250).
|
|
93
|
+
// Best effort: when that read fails the message names the decision, as before.
|
|
94
|
+
let view;
|
|
95
|
+
try {
|
|
96
|
+
view = await proposals_1.proposalApi.show(ref.kind, ref.id);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
view = undefined;
|
|
100
|
+
}
|
|
101
|
+
this.log((0, proposals_1.describeReplay)(decision, result, view));
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
this.log(`Proposal ${decision} decision recorded.`);
|
|
105
|
+
}
|
|
86
106
|
}
|
|
87
107
|
}
|
|
88
108
|
exports.ProposalDecisionCommand = ProposalDecisionCommand;
|
package/dist/lib/proposals.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ProposalDecisionParams, ProposalDecisionResponse, ProposalKind, ProposalListParams, ProposalListResponse, ProposalView } from '@skrr-ai/data-provider';
|
|
1
|
+
import type { ProposalDecision, ProposalDecisionParams, ProposalDecisionResponse, ProposalKind, ProposalListParams, ProposalListResponse, ProposalView } from '@skrr-ai/data-provider';
|
|
2
2
|
/** The unified, read-time proposal inbox. Decisions are routed by the server to
|
|
3
3
|
* each family's own writer; the CLI never writes a source row. */
|
|
4
4
|
export declare const proposalApi: {
|
|
@@ -23,3 +23,28 @@ export declare function resolveProposalRef(kindArg: string | undefined, idArg: s
|
|
|
23
23
|
};
|
|
24
24
|
/** A 404 here means no such proposal under THIS kind: the id is looked up under the kind given. */
|
|
25
25
|
export declare function describeProposalNotFound(kind: string, id: string, bin?: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* The intent's title in words: what it would DO, to what. Any other kind of proposal, and an
|
|
28
|
+
* intent the server sent no context for, keeps the view's own title.
|
|
29
|
+
*/
|
|
30
|
+
export declare function proposalTitleFor(view: ProposalView): string;
|
|
31
|
+
/**
|
|
32
|
+
* The inbox files a rejection nobody on the owner's side made under `discarded`. For a Northstar
|
|
33
|
+
* intent the verdict, and who made it, is said in the NOTE column, so a policy refusal is not
|
|
34
|
+
* read as something the person threw away.
|
|
35
|
+
*/
|
|
36
|
+
export declare function northstarIntentNote(view: ProposalView): string;
|
|
37
|
+
/**
|
|
38
|
+
* `proposals show` for a Northstar intent, as lines a person can read. Null for every other kind,
|
|
39
|
+
* which keeps printing the full JSON as before (a script reading it does not change).
|
|
40
|
+
*/
|
|
41
|
+
export declare function renderNorthstarIntentView(view: ProposalView): string[] | null;
|
|
42
|
+
/**
|
|
43
|
+
* What a replayed decision says. The replay receipt names the decision that was asked for ("keep"), but a
|
|
44
|
+
* Northstar intent that was kept has gone on: it may be applied, or approved and not applied yet, and a
|
|
45
|
+
* discard lands as `rejected`. "Already kept" for an intent that was in fact applied understated what
|
|
46
|
+
* had happened (OSK-21250), so the intent's own verdict is named when the proposal could be read.
|
|
47
|
+
*/
|
|
48
|
+
export declare function describeReplay(decision: ProposalDecision, receipt: {
|
|
49
|
+
decidedAt?: string;
|
|
50
|
+
}, view?: ProposalView): string;
|
package/dist/lib/proposals.js
CHANGED
|
@@ -3,8 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.proposalApi = void 0;
|
|
4
4
|
exports.resolveProposalRef = resolveProposalRef;
|
|
5
5
|
exports.describeProposalNotFound = describeProposalNotFound;
|
|
6
|
+
exports.proposalTitleFor = proposalTitleFor;
|
|
7
|
+
exports.northstarIntentNote = northstarIntentNote;
|
|
8
|
+
exports.renderNorthstarIntentView = renderNorthstarIntentView;
|
|
9
|
+
exports.describeReplay = describeReplay;
|
|
6
10
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
7
11
|
const data_provider_2 = require("@skrr-ai/data-provider");
|
|
12
|
+
const northstars_1 = require("./northstars");
|
|
8
13
|
const triggers_1 = require("./triggers");
|
|
9
14
|
const MOUNT = '/api/proposals';
|
|
10
15
|
/** The unified, read-time proposal inbox. Decisions are routed by the server to
|
|
@@ -61,3 +66,161 @@ function describeProposalNotFound(kind, id, bin = 'skrr') {
|
|
|
61
66
|
return (`No ${kind} proposal ${id} is visible to you. A proposal is looked up under its kind, so an id ` +
|
|
62
67
|
`that exists under another kind is not found here; \`${bin} proposals list\` prints each as KIND:ID.`);
|
|
63
68
|
}
|
|
69
|
+
/* --------------------------------------------------------------------------
|
|
70
|
+
* A Northstar intent, in words
|
|
71
|
+
*
|
|
72
|
+
* `proposals list` showed the intent's TITLE as its kind code (`pause_commitment`),
|
|
73
|
+
* `proposals show` printed the raw JSON, `rejected` read as `discarded` (the inbox's
|
|
74
|
+
* own vocabulary, which says a person discarded something), and the sentence the
|
|
75
|
+
* governor wrote lived only inside the JSON. Everything below renders fields the
|
|
76
|
+
* view already carries; no new data is needed.
|
|
77
|
+
* ------------------------------------------------------------------------ */
|
|
78
|
+
/** What each kind of intent does, as a phrase a target can follow. A new kind fails the build. */
|
|
79
|
+
const INTENT_PHRASE = {
|
|
80
|
+
pause_commitment: 'Pause',
|
|
81
|
+
resume_commitment: 'Resume',
|
|
82
|
+
reprioritize_commitment: 'Reprioritize',
|
|
83
|
+
request_commitment_check: 'Check now:',
|
|
84
|
+
set_execution_bounds: 'Change the run bounds of',
|
|
85
|
+
create_commitment_proposal: 'Start a new commitment',
|
|
86
|
+
reassign_commitment_proposal: 'Reassign',
|
|
87
|
+
};
|
|
88
|
+
/** The intent's own verdict, which the inbox flattens into proposed/kept/discarded/expired. */
|
|
89
|
+
const VERDICT_WORDS = {
|
|
90
|
+
advisory: 'advisory: recorded for you to read, nothing to decide',
|
|
91
|
+
awaiting_approval: 'waiting for your decision',
|
|
92
|
+
approved: 'approved, not applied yet',
|
|
93
|
+
applying: 'being applied',
|
|
94
|
+
applied: 'applied',
|
|
95
|
+
rejected: 'rejected',
|
|
96
|
+
invalidated: 'invalidated: it went stale before it was applied',
|
|
97
|
+
};
|
|
98
|
+
function isNorthstarIntentView(view) {
|
|
99
|
+
return view.kind === 'northstar_intent' && Boolean(view.decisionContext?.snapshot);
|
|
100
|
+
}
|
|
101
|
+
function intentTarget(view) {
|
|
102
|
+
const id = view.decisionContext?.snapshot.targetCommitmentId ?? view.commitmentId;
|
|
103
|
+
if (view.commitmentTitle)
|
|
104
|
+
return id ? `${view.commitmentTitle} (${id})` : view.commitmentTitle;
|
|
105
|
+
return id ?? 'a commitment';
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The intent's title in words: what it would DO, to what. Any other kind of proposal, and an
|
|
109
|
+
* intent the server sent no context for, keeps the view's own title.
|
|
110
|
+
*/
|
|
111
|
+
function proposalTitleFor(view) {
|
|
112
|
+
if (!isNorthstarIntentView(view))
|
|
113
|
+
return view.title;
|
|
114
|
+
const snapshot = view.decisionContext.snapshot;
|
|
115
|
+
const phrase = INTENT_PHRASE[snapshot.kind] ?? snapshot.kind;
|
|
116
|
+
if (snapshot.kind === 'create_commitment_proposal') {
|
|
117
|
+
const title = snapshot.payload?.title;
|
|
118
|
+
return typeof title === 'string' && title.trim() ? `${phrase}: ${title.trim()}` : phrase;
|
|
119
|
+
}
|
|
120
|
+
if (snapshot.kind === 'reassign_commitment_proposal') {
|
|
121
|
+
const agent = snapshot.payload?.agentId;
|
|
122
|
+
const to = typeof agent === 'string' && agent ? ` to agent ${agent}` : '';
|
|
123
|
+
return `${phrase} ${intentTarget(view)}${to}`;
|
|
124
|
+
}
|
|
125
|
+
return `${phrase} ${intentTarget(view)}`;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The inbox files a rejection nobody on the owner's side made under `discarded`. For a Northstar
|
|
129
|
+
* intent the verdict, and who made it, is said in the NOTE column, so a policy refusal is not
|
|
130
|
+
* read as something the person threw away.
|
|
131
|
+
*/
|
|
132
|
+
function northstarIntentNote(view) {
|
|
133
|
+
if (!isNorthstarIntentView(view))
|
|
134
|
+
return '';
|
|
135
|
+
const verdict = view.sourceStatus;
|
|
136
|
+
const reason = view.decision?.reason ? `: ${view.decision.reason}` : '';
|
|
137
|
+
if (verdict === 'rejected') {
|
|
138
|
+
return view.decision?.by ? `rejected by you${reason}` : `rejected by policy${reason}`;
|
|
139
|
+
}
|
|
140
|
+
if (verdict === 'invalidated') {
|
|
141
|
+
return `invalidated: it went stale, nothing was discarded${reason}`;
|
|
142
|
+
}
|
|
143
|
+
if (verdict === 'advisory')
|
|
144
|
+
return 'advisory: nothing to decide';
|
|
145
|
+
return '';
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* `proposals show` for a Northstar intent, as lines a person can read. Null for every other kind,
|
|
149
|
+
* which keeps printing the full JSON as before (a script reading it does not change).
|
|
150
|
+
*/
|
|
151
|
+
function renderNorthstarIntentView(view) {
|
|
152
|
+
if (!isNorthstarIntentView(view))
|
|
153
|
+
return null;
|
|
154
|
+
const context = view.decisionContext;
|
|
155
|
+
const verdict = view.sourceStatus;
|
|
156
|
+
const risk = context.riskLevel ?? context.snapshot.riskLevel ?? 'unknown';
|
|
157
|
+
const hasTarget = Boolean(context.snapshot.targetCommitmentId || view.commitmentId);
|
|
158
|
+
const lines = [proposalTitleFor(view), ''];
|
|
159
|
+
lines.push(`Proposal ${view.id}`);
|
|
160
|
+
lines.push(`Kind ${context.snapshot.kind} · risk ${risk}`);
|
|
161
|
+
lines.push(`Target ${hasTarget ? intentTarget(view) : 'none (a new commitment)'}`);
|
|
162
|
+
if (context.reason)
|
|
163
|
+
lines.push(`Why ${context.reason}`);
|
|
164
|
+
lines.push(`Status ${VERDICT_WORDS[verdict] ?? verdict} (the inbox files it under "${view.disposition}")`);
|
|
165
|
+
const decision = view.decision;
|
|
166
|
+
if (verdict === 'awaiting_approval' || verdict === 'advisory') {
|
|
167
|
+
// Nobody has decided: `decision.reason` here is the policy that HELD it (or kept it advisory),
|
|
168
|
+
// and printing it as "Decided … by policy" read as a recorded decision (OSK-21251).
|
|
169
|
+
if (decision?.reason) {
|
|
170
|
+
const words = (0, northstars_1.describeNorthstarReason)(decision.reason);
|
|
171
|
+
lines.push(verdict === 'awaiting_approval' ? `Waiting for you: ${words}` : `Not decided: ${words}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else if (decision && (decision.at || decision.by || decision.reason)) {
|
|
175
|
+
const at = decision.at ? `${(0, northstars_1.shortDate)(decision.at)} UTC` : 'at an unrecorded time';
|
|
176
|
+
const by = decision.by ? ` by user ${decision.by}` : ' by policy';
|
|
177
|
+
const via = decision.via ? ` via ${decision.via}` : '';
|
|
178
|
+
const reason = decision.reason ? `: ${decision.reason}` : '';
|
|
179
|
+
lines.push(`Decided ${at}${by}${via}${reason}`);
|
|
180
|
+
}
|
|
181
|
+
if (view.canDecide) {
|
|
182
|
+
lines.push(`Can decide ${view.availableDecisions.join(', ')}`);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
const why = view.undecidableReason
|
|
186
|
+
? data_provider_2.PROPOSAL_UNDECIDABLE_TEXT[view.undecidableReason]
|
|
187
|
+
: 'not now';
|
|
188
|
+
lines.push(`Can decide no: ${why}`);
|
|
189
|
+
}
|
|
190
|
+
const continuation = view.continuation;
|
|
191
|
+
if (continuation) {
|
|
192
|
+
const bits = [continuation.status, continuation.commitmentId, continuation.reason].filter(Boolean);
|
|
193
|
+
lines.push(`Continued ${bits.join(' · ')}`);
|
|
194
|
+
}
|
|
195
|
+
lines.push(`Revision ${JSON.stringify(view.revision)}`);
|
|
196
|
+
// The hint is for a proposal that can still be decided: beside "Can decide no" it contradicts
|
|
197
|
+
// the line above it (OSK-21255).
|
|
198
|
+
if (view.canDecide) {
|
|
199
|
+
lines.push(' (pass this exact JSON as --expected to keep, discard or snooze)');
|
|
200
|
+
}
|
|
201
|
+
lines.push(`Web ${view.links.web}`);
|
|
202
|
+
return lines;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* What a replayed decision says. The replay receipt names the decision that was asked for ("keep"), but a
|
|
206
|
+
* Northstar intent that was kept has gone on: it may be applied, or approved and not applied yet, and a
|
|
207
|
+
* discard lands as `rejected`. "Already kept" for an intent that was in fact applied understated what
|
|
208
|
+
* had happened (OSK-21250), so the intent's own verdict is named when the proposal could be read.
|
|
209
|
+
*/
|
|
210
|
+
function describeReplay(decision, receipt, view) {
|
|
211
|
+
let outcome = { keep: 'kept', discard: 'discarded', snooze: 'snoozed' }[decision];
|
|
212
|
+
if (view && isNorthstarIntentView(view)) {
|
|
213
|
+
const settled = INTENT_REPLAY_WORDS[view.sourceStatus];
|
|
214
|
+
if (settled)
|
|
215
|
+
outcome = settled;
|
|
216
|
+
}
|
|
217
|
+
const when = receipt.decidedAt ? ` at ${(0, northstars_1.shortDate)(receipt.decidedAt)} UTC` : '';
|
|
218
|
+
return `Already ${outcome}${when} — nothing changed.`;
|
|
219
|
+
}
|
|
220
|
+
/** The verdicts a replayed keep or discard can find an intent in. */
|
|
221
|
+
const INTENT_REPLAY_WORDS = {
|
|
222
|
+
applied: 'applied',
|
|
223
|
+
approved: 'approved (not applied yet)',
|
|
224
|
+
applying: 'approved and being applied',
|
|
225
|
+
rejected: 'rejected',
|
|
226
|
+
};
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
* `X-Idempotency-Key` (every `request.ts` / adapter write does). A 429 means the
|
|
11
11
|
* request was refused before it ran, so the key is belt and braces.
|
|
12
12
|
*
|
|
13
|
+
* Never retried when the body says it cannot help: a 429 that declares
|
|
14
|
+
* `retryable: false` (a follow-up budget refusal, which says the day resets at
|
|
15
|
+
* 00:00 UTC) is a refusal to keep, not a limiter to wait out. Retrying it sent the
|
|
16
|
+
* same refused request three times and told the server about three intents (OSK-21266).
|
|
17
|
+
*
|
|
13
18
|
* Bounded twice: at most `maxRetries` extra attempts and a per-wait ceiling, so
|
|
14
19
|
* a stuck limiter costs a script seconds, not minutes. `SKRR_RATE_LIMIT_RETRIES=0`
|
|
15
20
|
* turns it off.
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* `X-Idempotency-Key` (every `request.ts` / adapter write does). A 429 means the
|
|
12
12
|
* request was refused before it ran, so the key is belt and braces.
|
|
13
13
|
*
|
|
14
|
+
* Never retried when the body says it cannot help: a 429 that declares
|
|
15
|
+
* `retryable: false` (a follow-up budget refusal, which says the day resets at
|
|
16
|
+
* 00:00 UTC) is a refusal to keep, not a limiter to wait out. Retrying it sent the
|
|
17
|
+
* same refused request three times and told the server about three intents (OSK-21266).
|
|
18
|
+
*
|
|
14
19
|
* Bounded twice: at most `maxRetries` extra attempts and a per-wait ceiling, so
|
|
15
20
|
* a stuck limiter costs a script seconds, not minutes. `SKRR_RATE_LIMIT_RETRIES=0`
|
|
16
21
|
* turns it off.
|
|
@@ -65,6 +70,18 @@ function retryAfterOf(response) {
|
|
|
65
70
|
? parseRetryAfterMs(response.headers.get('retry-after'))
|
|
66
71
|
: undefined;
|
|
67
72
|
}
|
|
73
|
+
/** A 429 whose body declares `retryable: false`. Reads a clone, so the caller still gets the body. */
|
|
74
|
+
async function declaresNotRetryable(response) {
|
|
75
|
+
if (response.status !== 429)
|
|
76
|
+
return false;
|
|
77
|
+
try {
|
|
78
|
+
const body = (await response.clone().json());
|
|
79
|
+
return body?.retryable === false;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
68
85
|
/**
|
|
69
86
|
* Run `attemptFetch` and, on a 429 for a replay-safe request, wait and run it
|
|
70
87
|
* again. Returns the last response, with `retryAfterMs` of the final 429 (so the
|
|
@@ -80,6 +97,8 @@ async function fetchWithRateLimitRetry(attemptFetch, options) {
|
|
|
80
97
|
for (let attempt = 0; attempt < max && response.status === 429; attempt++) {
|
|
81
98
|
if (options.signal?.aborted)
|
|
82
99
|
break;
|
|
100
|
+
if (await declaresNotRetryable(response))
|
|
101
|
+
break;
|
|
83
102
|
await sleep(rateLimitWaitMs(attempt, retryAfterMs, options.random));
|
|
84
103
|
response = await attemptFetch();
|
|
85
104
|
retryAfterMs = retryAfterOf(response);
|
package/dist/lib/roles.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export declare const roleApi: {
|
|
|
53
53
|
config?: Record<string, unknown> | undefined;
|
|
54
54
|
}[];
|
|
55
55
|
sources: {
|
|
56
|
-
source: "space" | "tasks" | "memory" | "daemon" | "email" | "goal" | "files" | "local" | "browser" | "conversations" | "slack" | "github" | "messages" | "calendar" | "
|
|
56
|
+
source: "space" | "tasks" | "memory" | "daemon" | "email" | "goal" | "files" | "local" | "browser" | "conversations" | "slack" | "github" | "messages" | "calendar" | "docs" | "metrics" | "agent_run" | "agent_initiatives" | "documents" | "product_metrics" | "business_metrics" | "state_monitor" | "monitor";
|
|
57
57
|
enabled: boolean;
|
|
58
58
|
config?: Record<string, unknown> | undefined;
|
|
59
59
|
}[];
|
|
@@ -51,6 +51,15 @@ function charterLines(charter, { binName, spaceId, full = false }) {
|
|
|
51
51
|
if (lead) {
|
|
52
52
|
for (const line of lead.split('\n'))
|
|
53
53
|
lines.push(` ${line}`);
|
|
54
|
+
// Agents are handed about the first `leadContextChars` characters of the lead, then a pointer
|
|
55
|
+
// to the rest. The page printed all of it under "read by every agent", so a long lead looked
|
|
56
|
+
// fully delivered (the web card warns; the CLI said nothing). Measured like the server does.
|
|
57
|
+
const cap = charter.leadContextChars;
|
|
58
|
+
if (typeof cap === 'number' && cap > 0 && lead.length > cap) {
|
|
59
|
+
lines.push('');
|
|
60
|
+
lines.push(` Over the cap: this lead is ${lead.length} characters and agents get about the first ${cap}, ` +
|
|
61
|
+
'then a pointer to the rest. Put the essentials first, or move detail below a ## heading.');
|
|
62
|
+
}
|
|
54
63
|
}
|
|
55
64
|
else {
|
|
56
65
|
lines.push(' (empty: the page opens with a ## heading, so agents receive no charter lead. ' +
|
|
@@ -68,7 +68,6 @@ const fs = __importStar(require("node:fs"));
|
|
|
68
68
|
const http = __importStar(require("node:http"));
|
|
69
69
|
const https = __importStar(require("node:https"));
|
|
70
70
|
const path = __importStar(require("node:path"));
|
|
71
|
-
const node_buffer_1 = require("node:buffer");
|
|
72
71
|
const node_crypto_1 = require("node:crypto");
|
|
73
72
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
74
73
|
const base_command_1 = require("../base-command");
|
|
@@ -223,7 +222,7 @@ function taskAssetMarkdownRef(asset) {
|
|
|
223
222
|
*/
|
|
224
223
|
function fileFormData(filePath, filename, mimeType, role, ledger) {
|
|
225
224
|
const fd = new FormData();
|
|
226
|
-
fd.append('file', new
|
|
225
|
+
fd.append('file', new Blob([fs.readFileSync(filePath)], { type: mimeType }), filename);
|
|
227
226
|
if (role)
|
|
228
227
|
fd.append('role', role);
|
|
229
228
|
if (ledger?.identity) {
|
package/dist/lib/triggers.d.ts
CHANGED
|
@@ -508,7 +508,7 @@ export declare function formatDuplicateSubscriptionWarning(trigger: unknown): {
|
|
|
508
508
|
* "armed" when it will fire and "inert" until enabled — so a freshly-created
|
|
509
509
|
* trigger did not plainly confirm it is live.
|
|
510
510
|
*/
|
|
511
|
-
export declare function armedStateLabel(enabled: boolean | undefined): string;
|
|
511
|
+
export declare function armedStateLabel(enabled: boolean | undefined, lastResult?: string | null): string;
|
|
512
512
|
/**
|
|
513
513
|
* Human phrasing for an effect trigger's write mode (OSK-4886).
|
|
514
514
|
*
|
package/dist/lib/triggers.js
CHANGED
|
@@ -1241,8 +1241,16 @@ function formatDuplicateSubscriptionWarning(trigger) {
|
|
|
1241
1241
|
* "armed" when it will fire and "inert" until enabled — so a freshly-created
|
|
1242
1242
|
* trigger did not plainly confirm it is live.
|
|
1243
1243
|
*/
|
|
1244
|
-
function armedStateLabel(enabled) {
|
|
1245
|
-
|
|
1244
|
+
function armedStateLabel(enabled, lastResult) {
|
|
1245
|
+
if (enabled === false)
|
|
1246
|
+
return 'no — inert until enabled';
|
|
1247
|
+
// "Will fire" is the row's intent. A platform-managed row whose owner is fenced (Always-On off) ticks on
|
|
1248
|
+
// schedule and is skipped every time, and the same screen's `Fires:` line says so; the first line a reader
|
|
1249
|
+
// meets must not say the opposite (dogfood df-261031-commitment-authoring, OSK-21103).
|
|
1250
|
+
const skipped = typeof lastResult === 'string' ? /^skipped:(.+)$/.exec(lastResult) : null;
|
|
1251
|
+
return skipped
|
|
1252
|
+
? `yes — but its last tick was skipped (${skipped[1]}), so it did not run`
|
|
1253
|
+
: 'yes — will fire';
|
|
1246
1254
|
}
|
|
1247
1255
|
/**
|
|
1248
1256
|
* Human phrasing for an effect trigger's write mode (OSK-4886).
|
|
@@ -1340,7 +1348,7 @@ function fireRefusalLines(metadata) {
|
|
|
1340
1348
|
function renderTriggerSummary(trigger, log, options = {}) {
|
|
1341
1349
|
log(`ID: ${trigger.id ?? '-'}`);
|
|
1342
1350
|
log(`Kind: ${trigger.kind ?? '-'}`);
|
|
1343
|
-
log(`Armed: ${armedStateLabel(trigger.enabled)}`);
|
|
1351
|
+
log(`Armed: ${armedStateLabel(trigger.enabled, trigger.audit?.lastResult)}`);
|
|
1344
1352
|
log(`Scope: ${scopeLabel(trigger)}`);
|
|
1345
1353
|
/*
|
|
1346
1354
|
* Say when the row is not yours to change.
|