@skrr-ai/cli 0.1.127 → 0.1.129

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.
Files changed (165) hide show
  1. package/dist/base-command.d.ts +35 -0
  2. package/dist/base-command.js +107 -0
  3. package/dist/commands/agenda/trigger/list.d.ts +2 -1
  4. package/dist/commands/agenda/trigger/list.js +55 -6
  5. package/dist/commands/agents/chat.js +7 -0
  6. package/dist/commands/agents/create.js +3 -1
  7. package/dist/commands/browser/persistent-profile.d.ts +22 -0
  8. package/dist/commands/browser/persistent-profile.js +46 -0
  9. package/dist/commands/commitments/action-history.js +1 -1
  10. package/dist/commands/commitments/cases.js +3 -3
  11. package/dist/commands/commitments/check-now.d.ts +10 -2
  12. package/dist/commands/commitments/check-now.js +53 -3
  13. package/dist/commands/commitments/create.d.ts +6 -14
  14. package/dist/commands/commitments/create.js +64 -7
  15. package/dist/commands/commitments/doctor.js +3 -1
  16. package/dist/commands/commitments/effective-policy.d.ts +11 -1
  17. package/dist/commands/commitments/effective-policy.js +90 -4
  18. package/dist/commands/commitments/explain.js +10 -1
  19. package/dist/commands/commitments/hypotheses.js +12 -12
  20. package/dist/commands/commitments/next-tick.js +5 -0
  21. package/dist/commands/commitments/observations.js +18 -17
  22. package/dist/commands/commitments/pack/list.js +2 -1
  23. package/dist/commands/commitments/pack/plan.d.ts +6 -0
  24. package/dist/commands/commitments/pack/plan.js +19 -0
  25. package/dist/commands/commitments/plan.d.ts +14 -0
  26. package/dist/commands/commitments/plan.js +32 -0
  27. package/dist/commands/commitments/preflight.d.ts +20 -1
  28. package/dist/commands/commitments/preflight.js +118 -12
  29. package/dist/commands/commitments/show.d.ts +9 -0
  30. package/dist/commands/commitments/show.js +14 -1
  31. package/dist/commands/commitments/trigger-status.js +8 -0
  32. package/dist/commands/commitments/update.d.ts +1 -0
  33. package/dist/commands/commitments/update.js +45 -4
  34. package/dist/commands/commitments/wake-capabilities.js +13 -0
  35. package/dist/commands/compass/archive.js +4 -0
  36. package/dist/commands/compass/commitments.js +15 -1
  37. package/dist/commands/compass/create.js +11 -3
  38. package/dist/commands/compass/effective-policy.d.ts +1 -0
  39. package/dist/commands/compass/effective-policy.js +8 -7
  40. package/dist/commands/compass/revert.d.ts +8 -0
  41. package/dist/commands/compass/revert.js +25 -1
  42. package/dist/commands/compass/revisions.d.ts +6 -0
  43. package/dist/commands/compass/revisions.js +26 -2
  44. package/dist/commands/compass/tactic.d.ts +10 -3
  45. package/dist/commands/compass/tactic.js +22 -7
  46. package/dist/commands/compass/update.js +19 -3
  47. package/dist/commands/followups/act.js +1 -1
  48. package/dist/commands/followups/cancel.js +13 -0
  49. package/dist/commands/followups/observability.js +20 -1
  50. package/dist/commands/followups/plans/create.js +12 -1
  51. package/dist/commands/followups/show.js +13 -0
  52. package/dist/commands/followups/watch.js +1 -1
  53. package/dist/commands/goals/planner-config.d.ts +11 -0
  54. package/dist/commands/goals/planner-config.js +16 -0
  55. package/dist/commands/harnesses/list.js +1 -1
  56. package/dist/commands/machines/update.d.ts +5 -3
  57. package/dist/commands/machines/update.js +70 -9
  58. package/dist/commands/northstars/create.js +3 -1
  59. package/dist/commands/northstars/governance/configure.d.ts +4 -0
  60. package/dist/commands/northstars/governance/configure.js +76 -10
  61. package/dist/commands/northstars/intents/decide.d.ts +6 -0
  62. package/dist/commands/northstars/intents/decide.js +68 -15
  63. package/dist/commands/northstars/intents/list.d.ts +2 -1
  64. package/dist/commands/northstars/intents/list.js +15 -5
  65. package/dist/commands/northstars/intents.d.ts +26 -0
  66. package/dist/commands/northstars/intents.js +49 -0
  67. package/dist/commands/northstars/list.d.ts +1 -0
  68. package/dist/commands/northstars/list.js +24 -3
  69. package/dist/commands/northstars/recommendations/list.d.ts +1 -1
  70. package/dist/commands/northstars/recommendations/list.js +3 -3
  71. package/dist/commands/northstars/recommendations.d.ts +19 -0
  72. package/dist/commands/northstars/recommendations.js +42 -0
  73. package/dist/commands/northstars/review-now.d.ts +1 -0
  74. package/dist/commands/northstars/review-now.js +15 -4
  75. package/dist/commands/northstars/reviews.d.ts +1 -0
  76. package/dist/commands/northstars/reviews.js +34 -6
  77. package/dist/commands/northstars/update.js +8 -1
  78. package/dist/commands/proposals/list.js +11 -2
  79. package/dist/commands/proposals/show.js +9 -1
  80. package/dist/commands/spaces/auto-executor/preview.d.ts +14 -1
  81. package/dist/commands/spaces/auto-executor/preview.js +35 -1
  82. package/dist/commands/spaces/auto-executor/run.d.ts +17 -0
  83. package/dist/commands/spaces/auto-executor/run.js +60 -2
  84. package/dist/commands/spaces/auto-executor/runs.d.ts +9 -1
  85. package/dist/commands/spaces/auto-executor/runs.js +18 -1
  86. package/dist/commands/spaces/files/upload.js +1 -2
  87. package/dist/commands/spaces/heartbeats/create.js +7 -5
  88. package/dist/commands/spaces/heartbeats/update.js +5 -3
  89. package/dist/commands/spaces/index.js +23 -0
  90. package/dist/commands/store/artifacts/releases.d.ts +20 -0
  91. package/dist/commands/store/artifacts/releases.js +85 -0
  92. package/dist/commands/store/library.d.ts +14 -0
  93. package/dist/commands/store/library.js +71 -0
  94. package/dist/commands/tasks/list.d.ts +48 -0
  95. package/dist/commands/tasks/list.js +141 -9
  96. package/dist/commands/tasks/remind.js +23 -11
  97. package/dist/commands/tasks/retry-schedule.js +6 -3
  98. package/dist/commands/tasks/schedule.js +29 -7
  99. package/dist/commands/tasks/show.js +4 -0
  100. package/dist/generated/agent-guides.json +69 -69
  101. package/dist/lib/agent-trigger-listing.d.ts +26 -0
  102. package/dist/lib/agent-trigger-listing.js +63 -0
  103. package/dist/lib/agentic-stream.d.ts +9 -0
  104. package/dist/lib/agentic-stream.js +152 -41
  105. package/dist/lib/command-miss.js +11 -2
  106. package/dist/lib/commitment-attention-copy.d.ts +96 -0
  107. package/dist/lib/commitment-attention-copy.js +209 -0
  108. package/dist/lib/commitment-ledgers.js +3 -1
  109. package/dist/lib/commitment-loop-status.d.ts +6 -0
  110. package/dist/lib/commitment-loop-status.js +16 -6
  111. package/dist/lib/commitments.d.ts +112 -0
  112. package/dist/lib/commitments.js +250 -2
  113. package/dist/lib/compass-body-help.d.ts +20 -0
  114. package/dist/lib/compass-body-help.js +61 -0
  115. package/dist/lib/compass-refusals.d.ts +35 -0
  116. package/dist/lib/compass-refusals.js +50 -0
  117. package/dist/lib/compasses.d.ts +82 -4
  118. package/dist/lib/compasses.js +232 -12
  119. package/dist/lib/followups.d.ts +22 -1
  120. package/dist/lib/followups.js +21 -1
  121. package/dist/lib/format.d.ts +11 -0
  122. package/dist/lib/format.js +29 -0
  123. package/dist/lib/initiatives.d.ts +9 -2
  124. package/dist/lib/initiatives.js +13 -2
  125. package/dist/lib/iso-instant.d.ts +52 -0
  126. package/dist/lib/iso-instant.js +162 -0
  127. package/dist/lib/machines.d.ts +14 -1
  128. package/dist/lib/machines.js +24 -0
  129. package/dist/lib/northstars.d.ts +201 -24
  130. package/dist/lib/northstars.js +655 -27
  131. package/dist/lib/proposal-decision-command.js +21 -1
  132. package/dist/lib/proposals.d.ts +26 -1
  133. package/dist/lib/proposals.js +167 -0
  134. package/dist/lib/rate-limit-retry.d.ts +5 -0
  135. package/dist/lib/rate-limit-retry.js +19 -0
  136. package/dist/lib/roles.d.ts +1 -1
  137. package/dist/lib/space-charter.js +9 -0
  138. package/dist/lib/store-artifact-releases.d.ts +39 -0
  139. package/dist/lib/store-artifact-releases.js +12 -0
  140. package/dist/lib/store-library.d.ts +37 -0
  141. package/dist/lib/store-library.js +12 -0
  142. package/dist/lib/task-asset-upload.js +1 -2
  143. package/dist/lib/task-calendar.d.ts +19 -0
  144. package/dist/lib/task-calendar.js +72 -0
  145. package/dist/lib/task-recurrence-outcome.js +3 -1
  146. package/dist/lib/task-recurrence-summary.d.ts +15 -0
  147. package/dist/lib/task-recurrence-summary.js +228 -0
  148. package/dist/lib/task-reminder.d.ts +28 -0
  149. package/dist/lib/task-reminder.js +50 -0
  150. package/dist/lib/triggers.d.ts +13 -2
  151. package/dist/lib/triggers.js +31 -13
  152. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonWire.js +11 -1
  153. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/executorStopReceipt.d.ts +123 -0
  154. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/executorStopReceipt.js +160 -0
  155. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -0
  156. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +17 -0
  157. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonWire.js +11 -1
  158. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/executorStopReceipt.d.ts +123 -0
  159. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/executorStopReceipt.js +173 -0
  160. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -0
  161. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -0
  162. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  163. package/dist/node_modules/@skrr-ai/data-provider/index.js +6922 -6828
  164. package/oclif.manifest.json +612 -62
  165. 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
- this.log(flags.json ? JSON.stringify(result, null, 2) : `Proposal ${decision} decision recorded.`);
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;
@@ -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;
@@ -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,165 @@ 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
+ // The intent's own rating (`intentRisk`), the one `northstars intents list` prints under RISK.
157
+ const risk = (0, northstars_1.intentRisk)({ riskLevel: context.riskLevel ?? context.snapshot.riskLevel });
158
+ const hasTarget = Boolean(context.snapshot.targetCommitmentId || view.commitmentId);
159
+ const lines = [proposalTitleFor(view), ''];
160
+ lines.push(`Proposal ${view.id}`);
161
+ lines.push(`Kind ${context.snapshot.kind}`);
162
+ // Said once, here, because this is where a person who saw a lower "recommendation risk" on the
163
+ // recommendation this came from looks for the reason the two differ.
164
+ lines.push(`Risk ${risk} (rated for this change; the recommendation behind it has its own "recommendation risk")`);
165
+ lines.push(`Target ${hasTarget ? intentTarget(view) : 'none (a new commitment)'}`);
166
+ if (context.reason)
167
+ lines.push(`Why ${context.reason}`);
168
+ lines.push(`Status ${VERDICT_WORDS[verdict] ?? verdict} (the inbox files it under "${view.disposition}")`);
169
+ const decision = view.decision;
170
+ if (verdict === 'awaiting_approval' || verdict === 'advisory') {
171
+ // Nobody has decided: `decision.reason` here is the policy that HELD it (or kept it advisory),
172
+ // and printing it as "Decided … by policy" read as a recorded decision (OSK-21251).
173
+ if (decision?.reason) {
174
+ const words = (0, northstars_1.describeNorthstarReason)(decision.reason);
175
+ lines.push(verdict === 'awaiting_approval' ? `Waiting for you: ${words}` : `Not decided: ${words}`);
176
+ }
177
+ }
178
+ else if (decision && (decision.at || decision.by || decision.reason)) {
179
+ const at = decision.at ? `${(0, northstars_1.shortDate)(decision.at)} UTC` : 'at an unrecorded time';
180
+ const by = decision.by ? ` by user ${decision.by}` : ' by policy';
181
+ const via = decision.via ? ` via ${decision.via}` : '';
182
+ const reason = decision.reason ? `: ${decision.reason}` : '';
183
+ lines.push(`Decided ${at}${by}${via}${reason}`);
184
+ }
185
+ if (view.canDecide) {
186
+ lines.push(`Can decide ${view.availableDecisions.join(', ')}`);
187
+ }
188
+ else {
189
+ const why = view.undecidableReason
190
+ ? data_provider_2.PROPOSAL_UNDECIDABLE_TEXT[view.undecidableReason]
191
+ : 'not now';
192
+ lines.push(`Can decide no: ${why}`);
193
+ }
194
+ const continuation = view.continuation;
195
+ if (continuation) {
196
+ const bits = [continuation.status, continuation.commitmentId, continuation.reason].filter(Boolean);
197
+ lines.push(`Continued ${bits.join(' · ')}`);
198
+ }
199
+ lines.push(`Revision ${JSON.stringify(view.revision)}`);
200
+ // The hint is for a proposal that can still be decided: beside "Can decide no" it contradicts
201
+ // the line above it (OSK-21255).
202
+ if (view.canDecide) {
203
+ lines.push(' (pass this exact JSON as --expected to keep, discard or snooze)');
204
+ }
205
+ lines.push(`Web ${view.links.web}`);
206
+ return lines;
207
+ }
208
+ /**
209
+ * What a replayed decision says. The replay receipt names the decision that was asked for ("keep"), but a
210
+ * Northstar intent that was kept has gone on: it may be applied, or approved and not applied yet, and a
211
+ * discard lands as `rejected`. "Already kept" for an intent that was in fact applied understated what
212
+ * had happened (OSK-21250), so the intent's own verdict is named when the proposal could be read.
213
+ */
214
+ function describeReplay(decision, receipt, view) {
215
+ let outcome = { keep: 'kept', discard: 'discarded', snooze: 'snoozed' }[decision];
216
+ if (view && isNorthstarIntentView(view)) {
217
+ const settled = INTENT_REPLAY_WORDS[view.sourceStatus];
218
+ if (settled)
219
+ outcome = settled;
220
+ }
221
+ const when = receipt.decidedAt ? ` at ${(0, northstars_1.shortDate)(receipt.decidedAt)} UTC` : '';
222
+ return `Already ${outcome}${when} — nothing changed.`;
223
+ }
224
+ /** The verdicts a replayed keep or discard can find an intent in. */
225
+ const INTENT_REPLAY_WORDS = {
226
+ applied: 'applied',
227
+ approved: 'approved (not applied yet)',
228
+ applying: 'approved and being applied',
229
+ rejected: 'rejected',
230
+ };
@@ -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);
@@ -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" | "agent_run" | "docs" | "metrics" | "agent_initiatives" | "documents" | "product_metrics" | "business_metrics" | "state_monitor" | "monitor";
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. ' +
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `/api/store/artifacts/skill/:entityId/releases` — the seller's own
3
+ * publication history for one skill, newest release first.
4
+ *
5
+ * It answers the question a headless seller has after `store artifacts release`:
6
+ * where did my releases land, and did a curator admit or refuse one (with the
7
+ * note they left)? The source snapshot and its private storage references are
8
+ * never in this projection. One release in full is `store artifacts receipt`.
9
+ *
10
+ * The route is written out at its own call site rather than composed from a
11
+ * builder, like the rest of the Store CLI: the parity checker reads the LITERAL
12
+ * at the call site.
13
+ */
14
+ export type StoreArtifactReleaseSummary = {
15
+ releaseId: string;
16
+ releaseNumber: number;
17
+ versionLabel?: string;
18
+ status?: string;
19
+ admission?: {
20
+ state?: string;
21
+ review?: {
22
+ note?: string;
23
+ decidedAt?: string;
24
+ };
25
+ };
26
+ createdAt?: string;
27
+ updatedAt?: string;
28
+ publishedAt?: string;
29
+ };
30
+ export type StoreArtifactReleaseHistory = {
31
+ data: StoreArtifactReleaseSummary[];
32
+ /** Pass as `before` for the next (older) page; null when this is the last. */
33
+ nextBefore: number | null;
34
+ };
35
+ /** `GET /artifacts/skill/:entityId/releases` — `limit` 1-50 (default 10), `before` a release number. */
36
+ export declare function listSkillArtifactReleases(entityId: string, params?: {
37
+ limit?: number;
38
+ before?: number;
39
+ }): Promise<StoreArtifactReleaseHistory>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listSkillArtifactReleases = listSkillArtifactReleases;
4
+ const api_fetch_1 = require("./api-fetch");
5
+ const triggers_1 = require("./triggers");
6
+ /** `GET /artifacts/skill/:entityId/releases` — `limit` 1-50 (default 10), `before` a release number. */
7
+ async function listSkillArtifactReleases(entityId, params = {}) {
8
+ return (0, api_fetch_1.apiFetch)((0, triggers_1.withQuery)(`/api/store/artifacts/skill/${encodeURIComponent(entityId)}/releases`, {
9
+ limit: params.limit,
10
+ before: params.before,
11
+ }));
12
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * `/api/store/library/items` — the Skill, Action and Service copies the signed-in
3
+ * buyer holds in the active workspace, with whether each copy can still be used.
4
+ *
5
+ * Agent purchases are NOT here: `skrr store installs` reads those. This is the
6
+ * other three artifact kinds, which the web Library page lists and which had no
7
+ * headless read at all.
8
+ *
9
+ * The route is written out at its own call site rather than composed from a
10
+ * builder, like the rest of the Store CLI: the parity checker reads the LITERAL
11
+ * at the call site.
12
+ */
13
+ /** One bought copy. `copyAvailable` is false once the copy is suspended, revoked or uninstalled. */
14
+ export type StoreLibraryItem = {
15
+ /** The listing or release the buyer bought. */
16
+ sourceId: string;
17
+ /** The buyer's own installed copy; null while fulfillment has not produced one. */
18
+ copyId: string | null;
19
+ name: string | null;
20
+ copyAvailable: boolean;
21
+ /** The Agent the copy was installed onto (Skills and Actions only). */
22
+ targetAgentId?: string | null;
23
+ entitlementState?: string;
24
+ fulfillmentState?: string;
25
+ purchasedAt?: string;
26
+ };
27
+ export type StoreLibraryItems = {
28
+ skills: StoreLibraryItem[];
29
+ actions: StoreLibraryItem[];
30
+ services: StoreLibraryItem[];
31
+ };
32
+ /**
33
+ * `GET /library/items`. The workspace is required by the server (400
34
+ * `A workspace is required` without one), so it travels as the explicit
35
+ * header, the way `store installs` sends it.
36
+ */
37
+ export declare function getStoreLibraryItems(workspaceId?: string): Promise<StoreLibraryItems>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getStoreLibraryItems = getStoreLibraryItems;
4
+ const api_fetch_1 = require("./api-fetch");
5
+ /**
6
+ * `GET /library/items`. The workspace is required by the server (400
7
+ * `A workspace is required` without one), so it travels as the explicit
8
+ * header, the way `store installs` sends it.
9
+ */
10
+ async function getStoreLibraryItems(workspaceId) {
11
+ return (0, api_fetch_1.apiFetch)('/api/store/library/items', workspaceId ? { headers: { 'X-Workspace-Id': workspaceId } } : undefined);
12
+ }
@@ -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 node_buffer_1.Blob([fs.readFileSync(filePath)], { type: mimeType }), filename);
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) {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * What `skrr tasks list --calendar` tells a person about the answer it printed.
3
+ *
4
+ * The calendar read used to end with "Note: calendar occurrence expansion reached its safety cap."
5
+ * whatever had been cut, and "No calendar occurrences." whatever had been missed (OSK-21382,
6
+ * OSK-21510): no limit, no cutoff, no Task, no remedy, and the server's warning codes were never
7
+ * shown. The server now says which bounds it reached (`truncations`) and which access warnings
8
+ * apply; this words them.
9
+ */
10
+ import type { TaskCalendarResponse, TaskCalendarTruncation } from '@skrr-ai/data-provider';
11
+ export declare function describeCalendarTruncation(truncation: TaskCalendarTruncation): string;
12
+ export declare function describeCalendarWarning(code: string): string;
13
+ /**
14
+ * The lines to print after a calendar answer: empty when the read was complete. A calendar that
15
+ * was cut short never reads as complete, and every cut says what it was.
16
+ */
17
+ export declare function calendarNoticeLines(calendar: TaskCalendarResponse): string[];
18
+ /** The window that was actually read, in UTC, and what a date-only range stood for. */
19
+ export declare function calendarWindowLine(calendar: Pick<TaskCalendarResponse, 'from' | 'to'>): string;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describeCalendarTruncation = describeCalendarTruncation;
4
+ exports.describeCalendarWarning = describeCalendarWarning;
5
+ exports.calendarNoticeLines = calendarNoticeLines;
6
+ exports.calendarWindowLine = calendarWindowLine;
7
+ const who = (t) => t.ref || t.taskId || 'A task';
8
+ /**
9
+ * One sentence per bound. A `Record` over the shared vocabulary, so a code added to
10
+ * `TASK_CALENDAR_TRUNCATION_CODES` cannot be forgotten here.
11
+ */
12
+ const TRUNCATION_TEXT = {
13
+ task_occurrence_limit: (t) => `${who(t)} fires more than ${t.limit} times in this window (the limit is ${t.limit} per task)` +
14
+ `${t.cutoffAt ? `; its fires are listed up to ${t.cutoffAt} and later ones are not shown` : ''}. ` +
15
+ 'Narrow the range to see the rest.',
16
+ task_skipped_receipt_limit: (t) => `${who(t)} has more than ${t.limit} skipped-fire receipts in this window; only the latest ${t.limit} are shown.`,
17
+ task_recurrence_walk_limit: (t) => `${who(t)} has a run limit too far from this window to count what remains of it (more than ${t.limit} fires walked), so its fires in this window are not shown. ` +
18
+ 'Narrow the range to start closer to its next run.',
19
+ calendar_row_limit: (t) => `More than ${t.limit} rows matched${typeof t.omitted === 'number' ? `; ${t.omitted} are not shown` : ''}` +
20
+ `${t.cutoffAt ? `, and the rows are listed only up to ${t.cutoffAt}` : ''}. ` +
21
+ 'Narrow the range, or add --space or --project.',
22
+ task_candidate_limit: (t) => `More than ${t.limit} tasks are candidates for this window and not all were read. Narrow the range, or add --space or --project.`,
23
+ };
24
+ function describeCalendarTruncation(truncation) {
25
+ const words = TRUNCATION_TEXT[truncation.code];
26
+ // A code a newer server sent: say it rather than drop it.
27
+ return words
28
+ ? words(truncation)
29
+ : `The server reached a limit (${String(truncation.code)}, ${truncation.limit}) and did not describe it.`;
30
+ }
31
+ const WARNING_TEXT = {
32
+ space_cascade_capped: 'You can reach more spaces than one read covers, so tasks you did not create in the rest are not listed. Narrow with --space.',
33
+ space_access_capped: 'You can reach more spaces than one read covers, so tasks in the rest are not listed. Narrow with --space.',
34
+ };
35
+ function describeCalendarWarning(code) {
36
+ if (WARNING_TEXT[code])
37
+ return WARNING_TEXT[code];
38
+ if (code.endsWith('_partial')) {
39
+ return 'An access lookup failed, so some tasks may be missing. Retry the command.';
40
+ }
41
+ return `The server flagged this answer (${code}); it may be incomplete.`;
42
+ }
43
+ /**
44
+ * The lines to print after a calendar answer: empty when the read was complete. A calendar that
45
+ * was cut short never reads as complete, and every cut says what it was.
46
+ */
47
+ function calendarNoticeLines(calendar) {
48
+ const truncations = calendar.truncations ?? [];
49
+ const warnings = calendar.warnings ?? [];
50
+ const lines = [];
51
+ if (truncations.length > 0) {
52
+ lines.push('The calendar is incomplete:');
53
+ for (const truncation of truncations)
54
+ lines.push(` - ${describeCalendarTruncation(truncation)}`);
55
+ }
56
+ else if (calendar.truncated && warnings.length === 0) {
57
+ // A server that predates `truncations` flagged the answer and said nothing more.
58
+ lines.push('The calendar is incomplete: the server cut it short but did not say which limit it reached. Narrow the range, or add --space.');
59
+ }
60
+ if (warnings.length > 0) {
61
+ if (lines.length === 0)
62
+ lines.push('The calendar may be incomplete:');
63
+ for (const code of warnings) {
64
+ lines.push(` - Warning ${code}: ${describeCalendarWarning(code)}`);
65
+ }
66
+ }
67
+ return lines;
68
+ }
69
+ /** The window that was actually read, in UTC, and what a date-only range stood for. */
70
+ function calendarWindowLine(calendar) {
71
+ return `Window (UTC): ${calendar.from} .. ${calendar.to}. A date-only range is a whole UTC day.`;
72
+ }
@@ -22,7 +22,9 @@ function recurrenceRearmHint(code, taskRef = '<task-id>', bin = 'skrr') {
22
22
  }
23
23
  const ENDED_BECAUSE = {
24
24
  max_runs: 'it reached its maximum number of runs',
25
- end_date: 'its end date has passed',
25
+ // The end date is exclusive: it ends the series when it has passed AND when the next slot would
26
+ // land on or after it, so "has passed" would be false for a series ended one day early (OSK-21552).
27
+ end_date: 'its end date leaves no further occurrence',
26
28
  no_next_occurrence: 'its pattern has no further occurrence',
27
29
  };
28
30
  /** One line, or null when the Task does not recur (or an older server did not say). */
@@ -0,0 +1,15 @@
1
+ export type CronReading = {
2
+ /** "at 09:00 on the 13th of the month or on Friday" */
3
+ text: string;
4
+ /** True when both a day of month and a weekday are set: cron fires on EITHER. */
5
+ dayOfMonthOrWeekday: boolean;
6
+ };
7
+ /**
8
+ * Plain language for a five-field cron pattern, or `null` when this reader cannot say it faithfully.
9
+ */
10
+ export declare function describeCronPattern(pattern: string): CronReading | null;
11
+ /**
12
+ * The block `tasks show` prints for a recurring Task: the rule, its zone, a plain reading, when it
13
+ * next fires, how many runs it has used, and when it ends. Empty for a Task that does not recur.
14
+ */
15
+ export declare function recurrenceSummaryLines(task: Record<string, unknown>): string[];