@skrr-ai/cli 0.1.134 → 0.1.136

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.
@@ -454,7 +454,7 @@
454
454
  "id": "speaking",
455
455
  "title": "A Commitment finds; its Agent speaks",
456
456
  "readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
457
- "tokens": 301,
457
+ "tokens": 320,
458
458
  "aliases": [],
459
459
  "explains": []
460
460
  },
@@ -681,7 +681,7 @@
681
681
  "id": "idempotency-keys",
682
682
  "title": "Idempotency keys",
683
683
  "readWhen": "a send may be retried, or one answered `duplicate` or `idempotency_conflict`",
684
- "tokens": 202,
684
+ "tokens": 230,
685
685
  "aliases": [],
686
686
  "explains": [
687
687
  "idempotency_conflict"
@@ -709,7 +709,7 @@
709
709
  ]
710
710
  }
711
711
  ],
712
- "hash": "74f6b87c1bd1f851fbfe0b1ee5da04425d1b6fdafbf55445a39e121af9c4e65b",
712
+ "hash": "f49e16e088923137a5c6c8bd98891defefe0536246e03b3e5cc92ee4ea4e9eaf",
713
713
  "tokens": 545
714
714
  },
715
715
  "routedIndex": {
@@ -1167,7 +1167,7 @@
1167
1167
  "id": "speaking",
1168
1168
  "title": "A Commitment finds; its Agent speaks",
1169
1169
  "readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
1170
- "tokens": 301,
1170
+ "tokens": 320,
1171
1171
  "aliases": [],
1172
1172
  "explains": []
1173
1173
  },
@@ -1353,7 +1353,7 @@
1353
1353
  ]
1354
1354
  }
1355
1355
  ],
1356
- "hash": "6960740319e60127c5a1007fd32e77afada0b14442b5f6fb810a7fee1407cb63",
1356
+ "hash": "4981bf36d26a2768a45f209069e94372c0da86cc8658b706c3c77915fe5b5356",
1357
1357
  "tokens": 494
1358
1358
  },
1359
1359
  "cards": {
@@ -1747,7 +1747,7 @@
1747
1747
  "routed": true,
1748
1748
  "aliases": [],
1749
1749
  "body": "## Concept model\n\n| Noun | Question it answers |\n| --- | --- |\n| **Compass** | What does good look like for this Agent, and what may it never trade away? |\n| **Northstar** | Which Commitments should exist, at what priority and on what budget? |\n| **Commitment** | Which outcome does this Agent keep true, and how does it keep checking? |\n| **Check** | What did one wake of a Commitment see, decide and do? |\n\nA Commitment is not a unit of work. A **Task** is one unit of work and a **Goal** is a dated\noutcome with key results; a Commitment watches both and may create Tasks, and is neither.\n\n## Decide\n\nFive mechanisms make something happen later, and the noun alone does not pick one:\n\n| Mechanism | What it is for | The signal that picks it |\n| --- | --- | --- |\n| **Follow-up** | Coming back to one thing, once or a bounded few times: a reminder, a watch, a check-in, an agreed action | The ask ends: \"look tomorrow\", \"tell me when CI is green\" |\n| **Trigger** | Running the Agent with a set prompt on a schedule, an event, once, or on demand | The person names when, and every run does the same work |\n| **Heartbeat** | Checking in on a conversation, group chat or Space at a frequency that backs off while unread | The person wants regular contact, not an outcome held |\n| **Commitment** | Keeping an outcome true: watched sources, a cadence and a loop that decides when to act | \"Keep X true\", \"watch this metric\": no end, and stale evidence matters |\n| **Workflow** | Running a durable graph of Tasks in order | Repeatable work whose steps depend on each other |\n\nA one-off \"look at it tomorrow\" is a follow-up, never a Commitment. Standing observation is a\nCommitment with a freshness contract, not a recurring trigger or a task board left running.\n\n## Workflow\n\n### Start a Commitment\n\n1. Read the Agent's Commitments first, and change one rather than add a near-duplicate.\n2. Create it and turn it on in one call: the outcome, what it watches, how often it checks, where\n its work goes. Turning it on runs Preflight; a refusal keeps the draft and reports the code,\n reason and fix.\n3. Leave it a draft only when the person asked to see it first.\n4. Read it back, and tell the person what it watches, how often, and whether it is on.\n\n## Authority\n\n| Actor | May do | Only a person may do |\n| --- | --- | --- |\n| Agent | Read the Commitments of an Agent it can view; create one, change it and turn it on; request a check; pause one; decide whether a finding is worth telling the owner | Lift a stop a person made; set or clear a concurrency limit; choose the Task review policy; approve an action proposal or a cadence change; configure a Northstar's governance or decide its intents |\n| Person | Everything in both columns | — |\n\nA Commitment grants no permission: its runs work under the Agent's own. A Check never writes an\nowner-facing message, and a Compass never creates, rewrites, pauses or archives a Commitment. The\nserver cannot always tell an Agent from the person whose login it holds, so an accepted call is\nnot permission: the right-hand column stays a person's.\n\n## Live state\n\n- `skrr commitments list --agent <agent-id>` — an Agent's Commitments, with status and assessment.\n- `skrr commitments show <commitment-id>` — the contract, its next check and the last one.\n- `skrr commitments roster --agent <agent-id>` — each loop: whether it is armed, its next wake, runs today.\n\n## Related guides\n\n- `tasks` — the Tasks a Commitment creates or watches.\n- `spaces` — Goals, and Workflows: durable graphs of Tasks.\n- `agents` — the Agent a Commitment belongs to, and where its runs execute.\n- `communication` — whether, what and how to tell the owner.",
1750
- "hash": "197d9b84d1a4367ecf4b7a51687604d13814ec9e9265fe662883278a7738d2fa",
1750
+ "hash": "d9bbb541c0b955254d9795d98c26c1e7f075c98c5169c16574c457fde87fcec3",
1751
1751
  "tokens": 1169,
1752
1752
  "sections": [
1753
1753
  {
@@ -1824,7 +1824,7 @@
1824
1824
  "id": "speaking",
1825
1825
  "title": "A Commitment finds; its Agent speaks",
1826
1826
  "readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
1827
- "tokens": 301,
1827
+ "tokens": 320,
1828
1828
  "aliases": [],
1829
1829
  "explains": []
1830
1830
  },
@@ -2027,7 +2027,7 @@
2027
2027
  "routed": false,
2028
2028
  "aliases": [],
2029
2029
  "body": "## Concept model\n\nAn **Agent Update** is a deliberate owner-facing message. It is conversation, not\nrecord: what durably changed belongs to the work's own records, and the message\npoints at it.\n\n## Agent Updates\n\nProactively message the owner whenever it would improve their ability to steer the work, understand\na meaningful change, or stay confidently involved. Adapt to the task, uncertainty and stakes, the\nuser's engagement, what you have already communicated, and any per-agent style direction. Do not\nfollow a fixed schedule or manufacture status chatter.\n\n- Choose the semantic kind: `milestone` for a meaningful completed stage, `status` for a\n useful non-blocking checkpoint, `decision` for a concrete choice, `blocker` when user action is needed to\n continue, and one `result` for the terminal outcome (including failure).\n- Declare the attention intent with `--attention ambient|notify|interrupt`: `ambient` stays in\n the durable timeline by default, `notify` is worth seeing soon, and `interrupt` is reserved for\n work that is blocked or genuinely time-sensitive. The system and the user choose delivery channels and native\n presentation; attention never directly selects an Android channel, iOS interruption level, sound, or critical\n alert, and never bypasses notification preferences, quiet hours, Focus, or rate limits.\n- Say what is newly true and what it changes for the work. Include the next step, decision, or\n uncertainty when it would help the user; do not force every message into a fixed template.\n- Write like a capable teammate giving a busy owner a real, in-the-moment briefing — not like a\n system log, a customer-service script, or a task template. Open on the thing itself, never on a\n subject line (\"Status on the refactor:\", \"Quick update:\"). In the user's language, lead with the\n fresh fact, decision, or risk; then make clear why it matters and what happens next. Where the\n evidence is available, ground the update in one or two useful specifics: the affected thing, the\n evidence, the timing, or the trade-off.\n Use plain, varied sentences and only the confidence the evidence earns. Keep them short: where a\n dash would staple two clauses into one, use two sentences instead. Be warm and direct, but\n never manufacture familiarity, emotion, certainty, or colour that is not true. Avoid AI-shaped\n filler such as \"I am pleased to report\", \"completed / in progress / next step\" recaps,\n \"I will continue to monitor\", \"feel free to let me know\", ceremonial thanks, and vague claims\n such as \"the optimization is complete\". Say the thing instead: \"The migration is in; the\n slowest query fell from 2.8s to 420ms. I am running the full regression now and will flag any\n breakage before merge.\" Treat that as a standard of specificity, not a rigid script. Do not make\n a message longer merely to sound human.\n- Task context and card presentation are separate. A managed session's task context is derived\n server-side, but a message shows no Task Card unless you explicitly choose `--attach-task-card`\n (or `presentation.taskCard: true`). Use a card when it materially helps the user open or follow the\n live task; omit it for ordinary conversational updates.\n- In a hand-started session, use `skrr agent message context --agent <id>` when recent\n updates, read state, or a user's reply would help your judgment. It supplies observations, not a\n schedule or a command to send.\n- In a skrr-managed session, do not pass task, run, or session identity: skrr derives it.\n In a hand-started session, add `--task <task-id>` only when you know the correct task; it is\n declared context.\n- Prefer one message for one thought. For a conversational owner DM — such as a pitch, invitation,\n question, or progress update — when a draft exceeds roughly 320 characters or contains two independent thoughts,\n normally use a 2–4 message human-scale sequence with `--part \"<next thought>\"` and one explicit semantic\n `--idempotency-key`. Do not fragment a single thought merely to send more messages, and never split text\n mechanically: choose the semantic boundaries yourself. If one part does not reach the user, the remaining parts\n are deliberately not sent — the user would otherwise read your thoughts out of order.\n- An idempotency key names ONE update for this agent permanently, so make it specific to this run\n (for example `<task-id>:verify-complete`). Reuse it only to retry that same update: reused with\n the same text it is dropped as a duplicate, and with different text it is rejected.\n- A non-`delivered` status (`held`, `approval_required`, `duplicate`, `dropped`) is a policy\n outcome, not an error. It is never a reason to bypass this command with a direct DM or Slack\n call — this is the only sanctioned agent-to-user path.\n\n## Authority\n\n| Actor | May do | Only a person may do |\n| --- | --- | --- |\n| Agent | Decide to message; declare kind, attention intent, task context and a task card | Choose the delivery channel or native presentation; release a held or approval-gated intent |\n| Person | Everything an Agent may do | — |\n\n## Live state\n\n- `skrr agent message context --agent <id>` — recent updates, read state and owner engagement, as observations for the decision above.\n\n## Related guides\n\n- `tasks` — the records an update points at, and the durable version of what changed.\n- `agents` — which Agent a session speaks as; its `identity` section owns the agent-context refusals.",
2030
- "hash": "2ca0bfacfd5ac8edcfe5e36ca344d8698f02874e2786646c4f03bea28ca623d9",
2030
+ "hash": "d502e1cf74a97001200c86fb62c4eb43f9720a31ce99014376115db6202d198f",
2031
2031
  "tokens": 1359,
2032
2032
  "sections": [
2033
2033
  {
@@ -2063,7 +2063,7 @@
2063
2063
  "id": "idempotency-keys",
2064
2064
  "title": "Idempotency keys",
2065
2065
  "readWhen": "a send may be retried, or one answered `duplicate` or `idempotency_conflict`",
2066
- "tokens": 202,
2066
+ "tokens": 230,
2067
2067
  "aliases": [],
2068
2068
  "explains": [
2069
2069
  "idempotency_conflict"
@@ -5850,7 +5850,7 @@
5850
5850
  "id": "speaking",
5851
5851
  "title": "A Commitment finds; its Agent speaks",
5852
5852
  "readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
5853
- "tokens": 301,
5853
+ "tokens": 320,
5854
5854
  "aliases": [],
5855
5855
  "explains": []
5856
5856
  },
@@ -5966,7 +5966,7 @@
5966
5966
  "id": "speaking",
5967
5967
  "title": "A Commitment finds; its Agent speaks",
5968
5968
  "readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
5969
- "tokens": 301,
5969
+ "tokens": 320,
5970
5970
  "aliases": [],
5971
5971
  "explains": []
5972
5972
  },
@@ -6084,7 +6084,7 @@
6084
6084
  "id": "speaking",
6085
6085
  "title": "A Commitment finds; its Agent speaks",
6086
6086
  "readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
6087
- "tokens": 301,
6087
+ "tokens": 320,
6088
6088
  "aliases": [],
6089
6089
  "explains": []
6090
6090
  },
@@ -6206,7 +6206,7 @@
6206
6206
  "id": "speaking",
6207
6207
  "title": "A Commitment finds; its Agent speaks",
6208
6208
  "readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
6209
- "tokens": 301,
6209
+ "tokens": 320,
6210
6210
  "aliases": [],
6211
6211
  "explains": []
6212
6212
  },
@@ -6322,7 +6322,7 @@
6322
6322
  "id": "speaking",
6323
6323
  "title": "A Commitment finds; its Agent speaks",
6324
6324
  "readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
6325
- "tokens": 301,
6325
+ "tokens": 320,
6326
6326
  "aliases": [],
6327
6327
  "explains": []
6328
6328
  },
@@ -6438,7 +6438,7 @@
6438
6438
  "id": "speaking",
6439
6439
  "title": "A Commitment finds; its Agent speaks",
6440
6440
  "readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
6441
- "tokens": 301,
6441
+ "tokens": 320,
6442
6442
  "aliases": [],
6443
6443
  "explains": []
6444
6444
  },
@@ -6560,7 +6560,7 @@
6560
6560
  "id": "speaking",
6561
6561
  "title": "A Commitment finds; its Agent speaks",
6562
6562
  "readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
6563
- "tokens": 301,
6563
+ "tokens": 320,
6564
6564
  "aliases": [],
6565
6565
  "explains": []
6566
6566
  },
@@ -6598,9 +6598,9 @@
6598
6598
  "version": 4,
6599
6599
  "aliases": [],
6600
6600
  "explains": [],
6601
- "body": "Read this when a Commitment run found something and you must decide whether the owner hears of it.\n\nA Check never writes to the owner. When something it found may matter, the run is handed a brief\nunder \"Owner communication — your call.\": what was found, the evidence, why it may matter and a\nsuggested next step. Those are facts to judge, not a message to forward.\n\n- Decide with everything in view: what you already said, whether the owner replied, what another\n surface already raised. One message, a different message or nothing are all valid outcomes, and\n silence is often the right one.\n- To speak from the run, send it with `skrr agent message send` where the run allows it: it is\n delivered under the owner's limits and counted for this Commitment. Otherwise return a\n `status_update` or `report` action intent with its `text` for the Commitment's report\n destinations. A `message` action intent is recorded but never delivered. Write the words yourself.\n- The owner's limits (mute, quiet hours, a daily nudge cap) decide whether a run is asked to speak\n at all, and whatever you deliver counts against them, whichever way you send it.\n\n`skrr commitments reports <commitment-id>` lists where its reports go and on what condition; the\ndefault sends only on a material change.",
6602
- "hash": "45fa02a0344903cc355dff0b66eee45b14dab79a8f62fba8620106b29e11e8a9",
6603
- "tokens": 301,
6601
+ "body": "Read this when a Commitment run found something and you must decide whether the owner hears of it.\n\nA Check never writes to the owner, and it does not hand you a prepared message or brief: whether\nthe owner hears of what a run found is your call, from what you saw and what you already said.\n\n- Decide with everything in view: what you already said, whether the owner replied, what another\n surface already raised. One message, a different message or nothing are all valid outcomes, and\n silence is often the right one.\n- To speak from the run, send it with `skrr agent message send` where the run allows it: it is\n delivered under the owner's limits and counted for this Commitment. Otherwise return a\n `status_update` or `report` action intent with its `text` for the Commitment's report\n destinations. A `message` action intent is recorded but never delivered. Write the words yourself.\n- The owner's limits (mute, quiet hours, a daily nudge cap) apply when you deliver, whichever way\n you send it: whatever is delivered counts against them, and a delivery they do not allow is\n suppressed: the cycle shows it as a stop at \"reach\" where the platform recorded it, and `skrr agent\n message send` answers with its status.\n\n`skrr commitments reports <commitment-id>` lists where its reports go and on what condition; the\ndefault sends only on a material change.",
6602
+ "hash": "0e16cd15a9b31d7f5fbd477d623213e8c30d735c028841491691dab809b12c14",
6603
+ "tokens": 320,
6604
6604
  "sections": [
6605
6605
  {
6606
6606
  "id": "model",
@@ -6676,7 +6676,7 @@
6676
6676
  "id": "speaking",
6677
6677
  "title": "A Commitment finds; its Agent speaks",
6678
6678
  "readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
6679
- "tokens": 301,
6679
+ "tokens": 320,
6680
6680
  "aliases": [],
6681
6681
  "explains": []
6682
6682
  },
@@ -6796,7 +6796,7 @@
6796
6796
  "id": "speaking",
6797
6797
  "title": "A Commitment finds; its Agent speaks",
6798
6798
  "readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
6799
- "tokens": 301,
6799
+ "tokens": 320,
6800
6800
  "aliases": [],
6801
6801
  "explains": []
6802
6802
  },
@@ -6914,7 +6914,7 @@
6914
6914
  "id": "speaking",
6915
6915
  "title": "A Commitment finds; its Agent speaks",
6916
6916
  "readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
6917
- "tokens": 301,
6917
+ "tokens": 320,
6918
6918
  "aliases": [],
6919
6919
  "explains": []
6920
6920
  },
@@ -8043,7 +8043,7 @@
8043
8043
  "id": "idempotency-keys",
8044
8044
  "title": "Idempotency keys",
8045
8045
  "readWhen": "a send may be retried, or one answered `duplicate` or `idempotency_conflict`",
8046
- "tokens": 202,
8046
+ "tokens": 230,
8047
8047
  "aliases": [],
8048
8048
  "explains": [
8049
8049
  "idempotency_conflict"
@@ -8120,7 +8120,7 @@
8120
8120
  "id": "idempotency-keys",
8121
8121
  "title": "Idempotency keys",
8122
8122
  "readWhen": "a send may be retried, or one answered `duplicate` or `idempotency_conflict`",
8123
- "tokens": 202,
8123
+ "tokens": 230,
8124
8124
  "aliases": [],
8125
8125
  "explains": [
8126
8126
  "idempotency_conflict"
@@ -8159,9 +8159,9 @@
8159
8159
  "explains": [
8160
8160
  "idempotency_conflict"
8161
8161
  ],
8162
- "body": "Read this when a send may be retried, or one answered `duplicate` or `idempotency_conflict`.\n\nOmit `--idempotency-key` and the CLI derives one from the request — fine for a\none-shot send, but a retry that must dedup needs the explicit key the card\ndescribes.\n\n- The key is verbatim text, up to 240 characters.\n- `duplicate` in the result means the key already carried this identical update —\n the earlier send stands; do not resend.\n- `idempotency_conflict` means the key already carried different content or a\n different delivery policy — the update needs a new name.\n\n## Refusals\n\n| Code | Cause | Fix |\n| --- | --- | --- |\n| `idempotency_conflict` | The key was already used for a different message or delivery policy | Mint a key that names this run's update and retry |",
8163
- "hash": "5f09a95c12c779db800350c7e848cb3b87811a6e4fdc13c606d3bba20a36996d",
8164
- "tokens": 202,
8162
+ "body": "Read this when a send may be retried, or one answered `duplicate` or `idempotency_conflict`.\n\n`--idempotency-key` overrides an `idempotencyKey` (or `idempotency_key` /\n`idempotency-key`) in `--from-json`. When neither supplies a key, the CLI derives\none from the request. For a retry that must deduplicate, use the explicit key\nthe card describes.\n\n- The key is verbatim text, up to 240 characters.\n- `duplicate` in the result means the key already carried this identical update —\n the earlier send stands; do not resend.\n- `idempotency_conflict` means the key already carried different content or a\n different delivery policy — the update needs a new name.\n\n## Refusals\n\n| Code | Cause | Fix |\n| --- | --- | --- |\n| `idempotency_conflict` | The key was already used for a different message or delivery policy | Mint a key that names this run's update and retry |",
8163
+ "hash": "5ba623c4d77a8f3e7902ab455abd7e0413ef6351a4cc58a5e01a7b46cac58937",
8164
+ "tokens": 230,
8165
8165
  "sections": [
8166
8166
  {
8167
8167
  "id": "writing",
@@ -8196,7 +8196,7 @@
8196
8196
  "id": "idempotency-keys",
8197
8197
  "title": "Idempotency keys",
8198
8198
  "readWhen": "a send may be retried, or one answered `duplicate` or `idempotency_conflict`",
8199
- "tokens": 202,
8199
+ "tokens": 230,
8200
8200
  "aliases": [],
8201
8201
  "explains": [
8202
8202
  "idempotency_conflict"
@@ -8281,7 +8281,7 @@
8281
8281
  "id": "idempotency-keys",
8282
8282
  "title": "Idempotency keys",
8283
8283
  "readWhen": "a send may be retried, or one answered `duplicate` or `idempotency_conflict`",
8284
- "tokens": 202,
8284
+ "tokens": 230,
8285
8285
  "aliases": [],
8286
8286
  "explains": [
8287
8287
  "idempotency_conflict"
@@ -30,6 +30,11 @@ function proactiveBaselineLines(b) {
30
30
  ? `${quiet.start ?? '?'}-${quiet.end ?? '?'} ${quiet.timezone ?? '(no zone recorded)'}`
31
31
  : 'disabled'}`);
32
32
  }
33
+ const working = b.policy.workingHours;
34
+ if (working) {
35
+ const window = `${working.start ?? '?'}-${working.end ?? '?'} ${working.timezone ?? '(no zone recorded)'}`;
36
+ lines.push(`Working hours: ${working.enabled ? window : `disabled (${window})`}`);
37
+ }
33
38
  lines.push(`System loops: ${loops}`);
34
39
  lines.push(`Watchers: ${watchers}`);
35
40
  lines.push(`Model-run ceiling: ${b.dailyModelRunCeiling}/day (declared by the system loops)`);
@@ -7,6 +7,10 @@ export interface ProactiveWorkingHours {
7
7
  end: string;
8
8
  timezone?: string;
9
9
  }
10
+ /** Worker policy windows have an explicit enable switch; orchestrator hours do not. */
11
+ export interface ProactiveWorkerPolicyHours extends ProactiveWorkingHours {
12
+ enabled?: boolean;
13
+ }
10
14
  export interface SpecWatcher {
11
15
  source: WatcherSource;
12
16
  intervalMinutes?: number;
@@ -34,8 +38,8 @@ export interface ProactiveAgentSpec {
34
38
  maxAutoRunsPerDay?: number;
35
39
  cooldownMinutes?: number;
36
40
  requireApprovalRiskLevels?: string[];
37
- workingHours?: ProactiveWorkingHours;
38
- quietHours?: ProactiveWorkingHours;
41
+ workingHours?: ProactiveWorkerPolicyHours;
42
+ quietHours?: ProactiveWorkerPolicyHours;
39
43
  };
40
44
  };
41
45
  orchestrator?: {
@@ -228,6 +228,10 @@ export declare function isSmartScheduleProposal(value: unknown): value is SmartS
228
228
  * HTML error page, and dumping it into the user's terminal helps nobody.
229
229
  */
230
230
  export declare function describeNonProposal(value: unknown): string;
231
+ /** Explain terminal policy outcomes without changing the machine-readable status. */
232
+ export declare function todoPolicyBlockMessage(reason?: string): string | undefined;
233
+ /** Print explanations alongside the trace rows that recorded policy blocks. */
234
+ export declare function renderTodoPolicyTraceDetails(events: ScheduleTraceEvent[], log: (line: string) => void): void;
231
235
  /** Render a To-Do list as a table. */
232
236
  export declare function renderTodoList(todos: Todo[], log: (line: string) => void): void;
233
237
  /**
@@ -5,6 +5,8 @@ exports.scheduleTraceToday = scheduleTraceToday;
5
5
  exports.scheduleTracePath = scheduleTracePath;
6
6
  exports.isSmartScheduleProposal = isSmartScheduleProposal;
7
7
  exports.describeNonProposal = describeNonProposal;
8
+ exports.todoPolicyBlockMessage = todoPolicyBlockMessage;
9
+ exports.renderTodoPolicyTraceDetails = renderTodoPolicyTraceDetails;
8
10
  exports.renderTodoList = renderTodoList;
9
11
  exports.renderCapacityWarning = renderCapacityWarning;
10
12
  exports.renderProposal = renderProposal;
@@ -106,6 +108,27 @@ function describeNonProposal(value) {
106
108
  const keys = Object.keys(value);
107
109
  return keys.length === 0 ? 'empty object' : `object with keys: ${keys.slice(0, 8).join(', ')}`;
108
110
  }
111
+ /** Explain terminal policy outcomes without changing the machine-readable status. */
112
+ function todoPolicyBlockMessage(reason) {
113
+ switch (reason) {
114
+ case 'inside_quiet_hours':
115
+ return 'Not run: this time was inside the trigger’s quiet hours. Create a new To-Do to try again.';
116
+ case 'outside_working_hours':
117
+ return 'Not run: this time was outside the trigger’s working hours. Create a new To-Do to try again.';
118
+ default:
119
+ return undefined;
120
+ }
121
+ }
122
+ /** Print explanations alongside the trace rows that recorded policy blocks. */
123
+ function renderTodoPolicyTraceDetails(events, log) {
124
+ for (const event of events) {
125
+ const message = event.type === 'todo_execution_failed'
126
+ ? todoPolicyBlockMessage(event.reason)
127
+ : undefined;
128
+ if (message)
129
+ log(` ${event.id ?? event.summary ?? 'To-Do'}: ${message}`);
130
+ }
131
+ }
109
132
  /** Render a To-Do list as a table. */
110
133
  function renderTodoList(todos, log) {
111
134
  if (todos.length === 0) {
@@ -127,6 +150,11 @@ function renderTodoList(todos, log) {
127
150
  { key: 'source', header: 'SOURCE' },
128
151
  { key: 'id', header: 'ID', verbatim: true },
129
152
  ], log);
153
+ for (const todo of todos) {
154
+ const message = todo.status === 'failed' ? todoPolicyBlockMessage(todo.result?.errorMessage) : undefined;
155
+ if (message)
156
+ log(` ${todo.id}: ${message}`);
157
+ }
130
158
  }
131
159
  /**
132
160
  * Warn at REVIEW time when approving this plan is likely to hit the wall.
@@ -170,6 +170,8 @@ export interface TriggerFireRecord {
170
170
  durationMs?: number;
171
171
  finalizedAt?: string;
172
172
  metadata?: {
173
+ recordKind?: string;
174
+ afterOccurrenceId?: string;
173
175
  replayable?: boolean;
174
176
  replayBlockedReason?: string;
175
177
  replayRequirements?: string[];
@@ -581,6 +583,15 @@ export declare function renderTriggerSummary(trigger: TriggerDefinition, log: (l
581
583
  baseURL?: string;
582
584
  }): void;
583
585
  export declare function renderLegacyRuns(runs: LegacyTriggerRun[], log: (line: string) => void): void;
586
+ /** Human explanation for quiet-hour audit rows; a one-shot may have been re-armed. */
587
+ export declare function quietHoursAuditExplanation(fire: {
588
+ source?: unknown;
589
+ reason?: unknown;
590
+ outcome?: unknown;
591
+ }): {
592
+ summary: string;
593
+ detail: string;
594
+ } | null;
584
595
  export declare function renderFireAudit(fires: TriggerFireRecord[], log: (line: string) => void): void;
585
596
  /**
586
597
  * Trigger budget for one scope. `active` counts only ENABLED rows — a disabled
@@ -612,6 +623,10 @@ export declare const triggerApi: {
612
623
  get: (id: string) => Promise<TriggerDefinition>;
613
624
  update: (id: string, body: Record<string, unknown>) => Promise<TriggerDefinition>;
614
625
  setEnabled: (id: string, enabled: boolean) => Promise<TriggerDefinition>;
626
+ setAuthorizationRevoked: (id: string, revoked: boolean) => Promise<{
627
+ ok: boolean;
628
+ authorization?: Record<string, unknown>;
629
+ }>;
615
630
  delete: (id: string) => Promise<string>;
616
631
  fire: (id: string, body: Record<string, unknown>) => Promise<Record<string, unknown>>;
617
632
  /**
@@ -651,6 +666,17 @@ export declare const triggerApi: {
651
666
  agentId?: string;
652
667
  spaceId?: string;
653
668
  }) => Promise<TriggerQuotaStatus>;
669
+ prunePreview: (params: {
670
+ scope?: string;
671
+ agentId?: string;
672
+ spaceId?: string;
673
+ limit?: number;
674
+ }) => Promise<{
675
+ dryRun: true;
676
+ ownerWide: boolean;
677
+ wouldDelete: number;
678
+ ids: string[];
679
+ }>;
654
680
  /** Soft-delete the DISABLED triggers in a scope. Requires `confirm: true`. */
655
681
  prune: (body: {
656
682
  scope?: string;
@@ -53,6 +53,7 @@ exports.toolsRefusedHintLines = toolsRefusedHintLines;
53
53
  exports.fireRefusalLines = fireRefusalLines;
54
54
  exports.renderTriggerSummary = renderTriggerSummary;
55
55
  exports.renderLegacyRuns = renderLegacyRuns;
56
+ exports.quietHoursAuditExplanation = quietHoursAuditExplanation;
56
57
  exports.renderFireAudit = renderFireAudit;
57
58
  const data_provider_1 = require("@skrr-ai/data-provider");
58
59
  const node_fs_1 = require("node:fs");
@@ -1524,6 +1525,22 @@ function renderLegacyRuns(runs, log) {
1524
1525
  { key: 'conversation', header: 'CONVO', maxWidth: 24 },
1525
1526
  ], log);
1526
1527
  }
1528
+ /** Human explanation for quiet-hour audit rows; a one-shot may have been re-armed. */
1529
+ function quietHoursAuditExplanation(fire) {
1530
+ const reason = fire.reason || fire.outcome;
1531
+ if (reason !== 'inside_quiet_hours' && reason !== 'initiative_policy_quiet_hours')
1532
+ return null;
1533
+ if (fire.source === 'once') {
1534
+ return {
1535
+ summary: 'One-shot skipped: quiet hours',
1536
+ detail: 'This skipped occurrence will not retry automatically. Check the trigger state; if the one-shot is spent, schedule a new one outside quiet hours.',
1537
+ };
1538
+ }
1539
+ return {
1540
+ summary: 'Skipped: quiet hours',
1541
+ detail: 'Review the trigger and Agent quiet-hours settings before retrying.',
1542
+ };
1543
+ }
1527
1544
  function renderFireAudit(fires, log) {
1528
1545
  if (fires.length === 0) {
1529
1546
  log('No fire audit records.');
@@ -1534,7 +1551,9 @@ function renderFireAudit(fires, log) {
1534
1551
  firedAt: fire.firedAt ?? '-',
1535
1552
  outcome: fire.outcome ?? '-',
1536
1553
  source: fire.source ?? '-',
1537
- reason: fire.reason ?? fire.errorMessage ?? '',
1554
+ reason: fire.metadata?.recordKind === 'schedule_end_marker'
1555
+ ? 'Schedule ended after prior fire'
1556
+ : quietHoursAuditExplanation(fire)?.summary ?? fire.reason ?? fire.errorMessage ?? '',
1538
1557
  duration: fire.durationMs === undefined ? '-' : `${fire.durationMs}ms`,
1539
1558
  replay: fire.metadata?.replayable === true || fire.metadata?.replayContext
1540
1559
  ? 'yes'
@@ -1561,6 +1580,7 @@ exports.triggerApi = {
1561
1580
  setEnabled: (id, enabled) => data_provider_1.request.patch(`/api/triggers/${encodeURIComponent(id)}/enabled`, {
1562
1581
  enabled,
1563
1582
  }),
1583
+ setAuthorizationRevoked: (id, revoked) => data_provider_1.request.patch(`/api/triggers/${encodeURIComponent(id)}/authorization`, { revoked }),
1564
1584
  delete: (id) => data_provider_1.request.delete(`/api/triggers/${encodeURIComponent(id)}`),
1565
1585
  fire: (id, body) => data_provider_1.request.post(`/api/triggers/${encodeURIComponent(id)}/fire-now`, body),
1566
1586
  /**
@@ -1597,6 +1617,7 @@ exports.triggerApi = {
1597
1617
  * companion to `prune` — see `TriggerQuotaStatus`.
1598
1618
  */
1599
1619
  quota: (params) => data_provider_1.request.get(withQuery('/api/triggers/quota', params)),
1620
+ prunePreview: (params) => data_provider_1.request.get(withQuery('/api/triggers/prune/preview', params)),
1600
1621
  /** Soft-delete the DISABLED triggers in a scope. Requires `confirm: true`. */
1601
1622
  prune: (body) => data_provider_1.request.post('/api/triggers/prune', { ...body, confirm: true }),
1602
1623
  capabilities: (scope) => data_provider_1.request.get(`/api/triggers/scope-capabilities?scope=${encodeURIComponent(scope)}`),