toolroll 0.9.52 → 0.9.54

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 (187) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +55 -80
  3. package/dist/agentconfig.d.ts +11 -36
  4. package/dist/agentconfig.js +3 -62
  5. package/dist/approval-policy.d.ts +1 -1
  6. package/dist/approval-policy.js +1 -1
  7. package/dist/board.d.ts +3 -19
  8. package/dist/board.js +4 -42
  9. package/dist/browser/workspace.js +18 -18
  10. package/dist/browser-workspace.d.ts +2 -1
  11. package/dist/builder.d.ts +9 -40
  12. package/dist/builder.js +50 -257
  13. package/dist/chat-actions.js +3 -14
  14. package/dist/chat-ask.js +8 -8
  15. package/dist/chat-controls.d.ts +0 -4
  16. package/dist/chat-controls.js +0 -1
  17. package/dist/chat-core.d.ts +171 -0
  18. package/dist/chat-core.js +288 -0
  19. package/dist/chat-decide.d.ts +10 -8
  20. package/dist/chat-decide.js +27 -24
  21. package/dist/chat-delivery-state.d.ts +51 -75
  22. package/dist/chat-delivery-state.js +140 -170
  23. package/dist/chat-delivery.d.ts +21 -4
  24. package/dist/chat-delivery.js +313 -195
  25. package/dist/chat-flow.d.ts +29 -1
  26. package/dist/chat-flow.js +24 -50
  27. package/dist/chat-messages.d.ts +319 -0
  28. package/dist/chat-messages.js +841 -0
  29. package/dist/chat-migration.d.ts +30 -0
  30. package/dist/chat-migration.js +802 -0
  31. package/dist/chat-quiet.d.ts +3 -0
  32. package/dist/chat-quiet.js +7 -2
  33. package/dist/chat-task-actions.d.ts +1 -1
  34. package/dist/chat-task-actions.js +0 -2
  35. package/dist/claim.d.ts +79 -244
  36. package/dist/claim.js +688 -1023
  37. package/dist/cli.d.ts +2 -0
  38. package/dist/cli.js +10 -2
  39. package/dist/contracts/chat-content.d.ts +71 -2
  40. package/dist/contracts/chat-content.js +22 -3
  41. package/dist/contracts/chat-tables.d.ts +11 -4
  42. package/dist/contracts/chat-tables.js +165 -87
  43. package/dist/contracts/cli.d.ts +0 -13
  44. package/dist/contracts/cli.js +0 -2
  45. package/dist/contracts/console-api.d.ts +5 -10
  46. package/dist/contracts/console-api.js +4 -10
  47. package/dist/contracts/discord-callback.js +2 -1
  48. package/dist/contracts/flow.d.ts +50 -0
  49. package/dist/contracts/flow.js +26 -1
  50. package/dist/contracts/lead-tools.d.ts +0 -13
  51. package/dist/contracts/lead-tools.js +3 -3
  52. package/dist/contracts/slack-callback.js +3 -2
  53. package/dist/contracts/store-json.d.ts +1 -29
  54. package/dist/contracts/store-json.js +4 -17
  55. package/dist/contracts/teams-callback.js +2 -1
  56. package/dist/converse.d.ts +1 -11
  57. package/dist/converse.js +6 -38
  58. package/dist/coordinator.d.ts +0 -1
  59. package/dist/coordinator.js +3 -5
  60. package/dist/demo.js +16 -26
  61. package/dist/desktop-update-gate.js +0 -1
  62. package/dist/desktop-update.js +1 -3
  63. package/dist/discord-chat.js +19 -38
  64. package/dist/discord-settings.js +4 -13
  65. package/dist/discord.js +4 -14
  66. package/dist/dispatch.d.ts +1 -1
  67. package/dist/dispatch.js +1 -1
  68. package/dist/dispose.d.ts +2 -3
  69. package/dist/dispose.js +17 -92
  70. package/dist/envelope.js +0 -3
  71. package/dist/exec.d.ts +0 -64
  72. package/dist/exec.js +3 -356
  73. package/dist/exhaustion.d.ts +22 -76
  74. package/dist/exhaustion.js +14 -140
  75. package/dist/export.js +8 -8
  76. package/dist/flow-engine.js +1 -1
  77. package/dist/flow-schedule.d.ts +154 -0
  78. package/dist/flow-schedule.js +321 -0
  79. package/dist/flow-share.js +1 -1
  80. package/dist/flow-triggers.d.ts +6 -1
  81. package/dist/flow-triggers.js +65 -10
  82. package/dist/flows-cli.js +8 -4
  83. package/dist/flows-ui.js +2 -0
  84. package/dist/flows.js +1 -1
  85. package/dist/guides.js +9 -29
  86. package/dist/invoke.d.ts +1 -24
  87. package/dist/invoke.js +17 -259
  88. package/dist/lead-status.js +2 -4
  89. package/dist/lead-voice.js +1 -1
  90. package/dist/mate-doors.d.ts +1 -2
  91. package/dist/mate-doors.js +12 -25
  92. package/dist/mate-tools.d.ts +2 -2
  93. package/dist/mate-tools.js +13 -24
  94. package/dist/modes.d.ts +19 -8
  95. package/dist/modes.js +24 -14
  96. package/dist/notification-rows.d.ts +30 -0
  97. package/dist/notification-rows.js +102 -0
  98. package/dist/operate-remote-arguments.js +2 -6
  99. package/dist/operate-remote.js +6 -7
  100. package/dist/operate.d.ts +0 -6
  101. package/dist/operate.js +226 -2248
  102. package/dist/phase-routing.d.ts +13 -23
  103. package/dist/phase-routing.js +13 -35
  104. package/dist/plan-auto.js +2 -2
  105. package/dist/plane-review.js +5 -11
  106. package/dist/planner-source.js +2 -1
  107. package/dist/policy.d.ts +1 -4
  108. package/dist/policy.js +0 -8
  109. package/dist/process-recovery-eligibility.js +2 -3
  110. package/dist/project-delete.js +24 -41
  111. package/dist/proposal.d.ts +4 -24
  112. package/dist/proposal.js +3 -88
  113. package/dist/provider-auth.d.ts +3 -3
  114. package/dist/provider-auth.js +4 -6
  115. package/dist/provider.d.ts +11 -25
  116. package/dist/provider.js +4 -36
  117. package/dist/push.js +1 -1
  118. package/dist/recipe-ui.js +6 -6
  119. package/dist/recipes.d.ts +3 -3
  120. package/dist/recipes.js +27 -23
  121. package/dist/result-shots.js +2 -2
  122. package/dist/retention.js +1 -1
  123. package/dist/run-closeout.d.ts +1 -1
  124. package/dist/run-closeout.js +1 -4
  125. package/dist/scope.d.ts +11 -65
  126. package/dist/scope.js +17 -82
  127. package/dist/serve.js +15 -24
  128. package/dist/server/chat.js +7 -30
  129. package/dist/server/flows.js +11 -10
  130. package/dist/server/guards.js +0 -1
  131. package/dist/server/pages-assets.js +1 -1
  132. package/dist/server/pages.js +0 -2
  133. package/dist/server/people-tokens.js +0 -4
  134. package/dist/server/route-table.d.ts +1 -1
  135. package/dist/server/route-table.js +2 -17
  136. package/dist/server/settings.js +6 -12
  137. package/dist/server/shared.d.ts +28 -212
  138. package/dist/server/shared.js +71 -780
  139. package/dist/server/task-views.js +9 -76
  140. package/dist/server/tasks.d.ts +1 -50
  141. package/dist/server/tasks.js +36 -946
  142. package/dist/setup-guide.d.ts +2 -2
  143. package/dist/slack-api.d.ts +5 -1
  144. package/dist/slack-api.js +2 -2
  145. package/dist/slack-chat.d.ts +9 -6
  146. package/dist/slack-chat.js +29 -53
  147. package/dist/slack-settings.js +6 -15
  148. package/dist/slack.js +6 -16
  149. package/dist/store.d.ts +200 -1702
  150. package/dist/store.js +811 -5439
  151. package/dist/surface.js +10 -24
  152. package/dist/task-composer.d.ts +1 -1
  153. package/dist/task-composer.js +1 -1
  154. package/dist/task-control.d.ts +3 -10
  155. package/dist/task-control.js +2 -7
  156. package/dist/teammate-desk.js +1 -1
  157. package/dist/teammate-question.js +12 -12
  158. package/dist/teammates-ui.js +1 -1
  159. package/dist/teams-chat.js +12 -9
  160. package/dist/teams-settings.js +2 -2
  161. package/dist/teams.js +4 -5
  162. package/dist/telegram-flow.js +5 -5
  163. package/dist/telegram-mate.d.ts +0 -3
  164. package/dist/telegram-mate.js +0 -1
  165. package/dist/telegram.js +4 -4
  166. package/dist/templates.d.ts +11 -7
  167. package/dist/templates.js +12 -9
  168. package/dist/toolroll-update.d.ts +20 -0
  169. package/dist/toolroll-update.js +104 -5
  170. package/dist/work-index.js +2 -6
  171. package/dist/workspace-revision.js +3 -1
  172. package/dist/workspace-ui.js +1 -1
  173. package/package.json +1 -1
  174. package/scripts/recovery-canary.mjs +3 -3
  175. package/dist/contest.d.ts +0 -318
  176. package/dist/contest.js +0 -753
  177. package/dist/held.d.ts +0 -150
  178. package/dist/held.js +0 -799
  179. package/dist/liveness.d.ts +0 -30
  180. package/dist/liveness.js +0 -42
  181. package/dist/pricing.d.ts +0 -45
  182. package/dist/pricing.js +0 -77
  183. package/dist/routine.d.ts +0 -228
  184. package/dist/routine.js +0 -764
  185. package/dist/slack-state.d.ts +0 -13
  186. package/dist/slack-state.js +0 -10
  187. package/dist/supervisor.mjs +0 -297
@@ -1,152 +1,26 @@
1
1
  /**
2
- * The terminal exhaustion taxonomy (fallback chains, R1/C5/C8 of the
3
- * approved design). A finished attempt is classified into ONE of these,
4
- * where the evidence still exists — the provider adapter's structural
5
- * record plus the gateway's authoritative CLI version and auth mode —
6
- * NEVER in disposal, by which time the structural signal is gone.
2
+ * How a finished attempt ended, classified where the evidence still exists
3
+ * (the provider adapter's structural terminal record), never in disposal.
4
+ * The one class that changes what happens next is `auth-expired`: a sign-in
5
+ * or API key that no longer works pauses dispatch for that provider until
6
+ * someone signs in, instead of burning attempts. Everything else is the
7
+ * ordinary failure road.
7
8
  *
8
- * The whole feature is FAIL CLOSED by construction: a recognizer's
9
- * eligible-signal set is EMPTY until a versioned fixture, captured from a
10
- * GENUINELY EXHAUSTED subscription, proves the exact bytes. A build with
11
- * no fixture for a (provider, version) can therefore NEVER return an
12
- * eligible class — so it can never authorize a paid fallback. A
13
- * successful run cannot establish the exhausted shape; only real
14
- * exhaustion can, and the plane cannot manufacture it.
15
- *
16
- * Precedence (C5), applied by the classifier:
17
- * - A present STRUCTURAL FAILURE terminal blocks success ingestion even
18
- * on exit 0 (that is the parser's job; this module reads the record).
19
- * - Only an ALLOWLISTED, version+auth-specific pattern yields an ELIGIBLE
20
- * class. Exit code, bare rate-limit prose, timeout, notFound, stderr,
21
- * and zero tokens NEVER independently establish exhaustion.
22
- * - Everything unmatched is `unknown` — the ordinary failure road.
23
- * - A sign-in that no longer works (`auth-expired`) is recognized FIRST,
24
- * for every provider and version: an expired or revoked login is not a
25
- * property of a CLI version, and retrying it only burns attempts. It is
26
- * never fallback-eligible — a lapsed login must not reach a paid key.
27
- */
28
- /** The two classes that may authorize a fallback advance. */
29
- export const FALLBACK_ELIGIBLE = new Set([
30
- "usage-exhausted",
31
- "credits-depleted",
32
- ]);
33
- export function isFallbackEligible(cls) {
34
- return FALLBACK_ELIGIBLE.has(cls);
35
- }
36
- /**
37
- * The registry. EVERY entry ships empty (fail closed). A real fixture,
38
- * once captured from a genuinely exhausted subscription and reviewed,
39
- * adds the exact patterns under that provider+version — and ONLY then can
40
- * that (provider, version) ever return an eligible class.
41
- *
42
- * The outer key is the provider; the inner key is the exact CLI version
43
- * string the gateway proved at spawn (never a range — a version this
44
- * build has no fixture for is unrecognized, so fail closed).
45
- */
46
- const RECOGNIZERS = {
47
- claude: {},
48
- codex: {},
49
- openrouter: {},
50
- gemini: {},
51
- };
52
- /**
53
- * The recognizer lookup. Reads ONLY the module-level RECOGNIZERS registry —
54
- * there is NO mutation surface, so no build (production or otherwise) can
55
- * install a recognizer at runtime; a real fixture arrives as a source edit,
56
- * reviewed in a text diff (Codex E2/E3 review, finding 1). The suite proves
57
- * the eligible path by MOCKING this module, never by a shipped seam.
58
- * Object.hasOwn keeps the lookup TOTAL for adversarial version strings
59
- * (foundation review, finding 3): "__proto__" / "constructor" / "toString"
60
- * resolve to "no recognizer", never an inherited property.
61
- */
62
- function recognizersFromRegistry(provider, version) {
63
- if (!Object.hasOwn(RECOGNIZERS[provider], version))
64
- return undefined;
65
- return RECOGNIZERS[provider][version];
66
- }
67
- /** Whether THIS build carries a fixture-backed recognizer for a
68
- * (provider, version). The C8 gate consults this at every authority
69
- * point — not only at classification — so a downgrade to an unfixtured
70
- * build severs any in-flight fallback authority. */
71
- export function hasRecognizer(provider, version) {
72
- if (version === null)
73
- return false;
74
- const perVersion = recognizersFromRegistry(provider, version);
75
- if (perVersion === undefined)
76
- return false;
77
- return perVersion.eligibleUsage.length > 0 || perVersion.eligibleCredits.length > 0;
78
- }
79
- /**
80
- * The EXACT C8 capability check (Codex E2/E3 review, finding 8): whether
81
- * this build can recognize the specific eligible class an AUTH MODE would
82
- * produce — a subscription's `usage-exhausted` needs a nonempty
83
- * eligibleUsage set; an api-key account's `credits-depleted` needs a
84
- * nonempty eligibleCredits set. `hasRecognizer` (any-eligible) is too coarse
85
- * at the authority point: a version fixtured for usage only must NOT
86
- * authorize a credits-depleted advance.
87
- */
88
- export function recognizesEligible(provider, version, authMode) {
89
- if (version === null)
90
- return false;
91
- const perVersion = recognizersFromRegistry(provider, version);
92
- if (perVersion === undefined)
93
- return false;
94
- return authMode === "subscription" ? perVersion.eligibleUsage.length > 0 : perVersion.eligibleCredits.length > 0;
95
- }
96
- /** The eligible class an auth mode can legitimately carry — the C8
97
- * consistency invariant: `usage-exhausted` iff subscription,
98
- * `credits-depleted` iff api-key. A stamp that violates this pairing is a
99
- * bad/overwritten record and must never advance. */
100
- export function classMatchesAuthMode(cls, authMode) {
101
- if (cls === "usage-exhausted")
102
- return authMode === "subscription";
103
- if (cls === "credits-depleted")
104
- return authMode === "api-key";
105
- return false;
106
- }
107
- /**
108
- * Classify a finished attempt. Fail closed: no recognizer for this exact
109
- * (provider, version) => never an eligible class. `authMode` decides which
110
- * eligible class a match yields — a subscription that hits its cap is
111
- * `usage-exhausted`; an api-key account out of credits is
112
- * `credits-depleted`. A structural failure with no eligible/transient
113
- * match is `not-exhausted`; no structural failure and no match is
114
- * `unknown`.
9
+ * `usage-exhausted`, `credits-depleted` and `transient-throttle` are only
10
+ * ever read from older runs' records (they were stamped by a removed
11
+ * feature); this build never produces them.
115
12
  */
13
+ /** Classify a finished attempt: a sign-in that no longer works first, for
14
+ * any provider or version; then a structural failure terminal is
15
+ * `not-exhausted`, and anything else `unknown`. */
116
16
  export function classifyTerminal(args) {
117
- const { provider, version, authMode, terminal } = args;
118
- // A sign-in that no longer works, before anything else and for any
119
- // provider or version: no fixture is needed to know a login is gone.
17
+ const { terminal } = args;
120
18
  if (terminal !== null && terminal.failed === true && isAuthFailure(`${terminal.code ?? ""}\n${terminal.text ?? ""}`))
121
19
  return "auth-expired";
122
20
  if (typeof args.earlyExit === "string" && isAuthFailure(args.earlyExit))
123
21
  return "auth-expired";
124
- if (terminal === null)
125
- return "unknown";
126
- // The classifier's contract permits failed:false, but a non-failure
127
- // terminal is never exhaustion (Codex foundation review, finding 4):
128
- // only a structural FAILURE terminal is even eligible for matching.
129
- if (terminal.failed !== true)
22
+ if (terminal === null || terminal.failed !== true)
130
23
  return "unknown";
131
- const perVersion = version === null ? undefined : recognizersFromRegistry(provider, version);
132
- const haystack = `${terminal.code ?? ""}\n${terminal.text ?? ""}`;
133
- if (perVersion !== undefined) {
134
- // Transient throttle is checked FIRST so a throttle can never be read
135
- // as exhaustion (Claude's "temporarily limiting requests — not your
136
- // usage limit" family).
137
- if (perVersion.transient.some(re => re.test(haystack)))
138
- return "transient-throttle";
139
- // Then the eligible sets, gated by auth mode.
140
- if (authMode === "subscription" && perVersion.eligibleUsage.some(re => re.test(haystack))) {
141
- return "usage-exhausted";
142
- }
143
- if (authMode === "api-key" && perVersion.eligibleCredits.some(re => re.test(haystack))) {
144
- return "credits-depleted";
145
- }
146
- }
147
- // A structural FAILURE terminal that matched nothing is a definite
148
- // non-exhaustion terminal (a non-failure terminal already returned
149
- // unknown above).
150
24
  return "not-exhausted";
151
25
  }
152
26
  /**
package/dist/export.js CHANGED
@@ -29,20 +29,20 @@ import { readEmailSettings } from "./email-settings.js";
29
29
  import { SUBSCRIPTION_CAPABLE, readAuthMode } from "./keys.js";
30
30
  export const EXPORT_FORMAT = "standing-orders/export/v1";
31
31
  /** Tables that hold nothing but secrets or short-lived sign-in material, and derived search indexes. */
32
- const SECRET_TABLES = new Set(["web_session", "ceremony_nonce", "telegram_pairing", "slack_pair", "discord_pair", "teams_pair", "oauth_code", "oauth_refresh"]);
32
+ const SECRET_TABLES = new Set(["web_session", "ceremony_nonce", "chat_pair", "oauth_code", "oauth_refresh"]);
33
33
  const DERIVED_TABLE = /^(sqlite_|memory_search)/;
34
34
  /** Columns that hold a secret (or what stands in for one): never exported. */
35
35
  const SECRET_COLUMNS = new Set([
36
36
  "api_token.secret_hash", "lead_credential.secret_hash", "approver.credential_hash", "coordinator_credential.credential_hash", "runner.credential_hash", "invite.token_hash",
37
- "flow_trigger.hook_hash", "held_session.cookie", "push_subscription.endpoint", "push_subscription.p256dh", "push_subscription.auth",
38
- "chat_turn.credential_key", "mate_session.credential_key", "mate_turn.credential_key", "bridge_lease.push_url", "workflow_preview.token",
37
+ "flow_trigger.hook_hash", "push_subscription.endpoint", "push_subscription.p256dh", "push_subscription.auth",
38
+ "chat_turn.credential_key", "mate_session.credential_key", "mate_turn.credential_key", "chat_runtime.push_url", "workflow_preview.token",
39
39
  "quota.credential_fp", "oauth_grant.renew_hash", "oauth_client.source_hash",
40
40
  ]);
41
41
  /** A column named like this holds a secret unless it is reviewed below; an unreviewed one is dropped. */
42
42
  const SECRET_NAME = /hash|secret|token|passw|cookie|csrf|credential|p256dh|^auth$|key|nonce|endpoint|url/i;
43
43
  /** Reviewed: named like a secret, but it isn't one (a seal, a digest, a lookup name, an address with no key). */
44
44
  const REVIEWED_COLUMNS = new Set([
45
- "artifact.key", "budget.scope_key", "mate_thread.scope_key", "diff_comment.source_key", "discord_meta.key", "slack_meta.key", "teams_meta.key",
45
+ "artifact.key", "budget.scope_key", "mate_thread.scope_key", "diff_comment.source_key", "chat_meta.key",
46
46
  "flow_trigger_event.key", "installation_fact.key", "mcp_idempotency.key", "memory_gap.key", "mutation.idempotency_key", "notification.dedupe_key",
47
47
  "service_cursor.key", "ledger_checkpoint.hash", "ledger_seal.hash", "merge_intent.grant_terms_hash", "plan_revision.parent_hash",
48
48
  "publication.body_hash", "publication.pr_url", "team_message.payload_hash", "team_request.payload_hash", "backend_grant.credential_scope",
@@ -54,10 +54,10 @@ export function categoryOf(table) {
54
54
  const rules = [
55
55
  [/^(action_ledger|ledger_seal|ledger_checkpoint|sync_ledger)$/, "ledger"],
56
56
  [/^teammate/, "teammates"],
57
- [/^(flow|routine|workflow_)/, "flows"],
58
- [/^(chat_|mate_|team_|telegram_|slack_|discord_|teams_|notification|push_|bridge_lease)/, "chats"],
57
+ [/^(flow|workflow_)/, "flows"],
58
+ [/^(chat_|mate_|team_|notification|push_)/, "chats"],
59
59
  [/^(approver|api_token|lead_credential|invite|sso_identity|coordinator_|oauth_)/, "people"],
60
- [/^(run|artifact|claim|worktree$|execution_slot|contest|tournament_terms|fallback_cycle|fallback_transition|held_session|session_turn|attended_authorization|criterion_review|diff_comment|proof_|review_request|repair_chain|incident|publication$|merge_|side_spend|knowledge_snapshot|learning_snapshot|skill_snapshot)/, "runs"],
60
+ [/^(run|artifact|claim|worktree$|execution_slot|criterion_review|diff_comment|proof_|review_request|repair_chain|incident|publication$|merge_|side_spend|knowledge_snapshot|learning_snapshot|skill_snapshot)/, "runs"],
61
61
  [/^(task|hold$|plan_|scope_|tool_seal|decision|external_|skill_test)/, "tasks"],
62
62
  [/^(project|approval_policy|capability|verify_command|worktree_setup|backend_grant|intake_grant|publication_grant|operating_mode|knowledge_|learning_|memory_|skill_)/, "projects"],
63
63
  [/(_config|_default|_defaults)$|^(budget|model_|provider_|installation_fact|runtime_check|monitoring_status|integration_check|quota|mode_rail|schema_version)/, "settings"],
@@ -215,7 +215,7 @@ Everything this installation knew on ${now.toISOString().slice(0, 16).replace("T
215
215
  - \`ledger/\`: the action ledger (\`action_ledger\`), each entry's seal (\`ledger_seal\`) and the checkpoints (\`ledger_checkpoint\`).
216
216
  - \`evidence-packs/\`: one JSON file per task: who asked, the exact terms and who approved them, runs and cost, checks, completion and its ledger entries.
217
217
  - \`chats/\`: conversations with the lead and teammates, and Telegram, Slack, Discord and Teams messages and notifications.
218
- - \`flows/\`: flows, their cards, scripts and triggers, routines and saved recipes.
218
+ - \`flows/\`: flows, their cards, scripts and triggers (schedules included) and saved recipes.
219
219
  - \`teammates/\`: teammates, their versions, memory, tools and turns.
220
220
  - \`people/\`: accounts, API token names and invitations (no passwords or tokens).
221
221
  - \`settings/\`: models, budgets, spend and permission defaults, provider readings; \`files.json\` holds the settings kept in files.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The flow engine: moves cards through their flow's zones. Deterministic
3
- * and model-free — it runs in a worker's pass beside routines, files work
3
+ * and model-free — it runs in a worker's pass beside builds, files work
4
4
  * through the ordinary task door (so every approval and check a task needs
5
5
  * still applies), reads task state, posts messages through the outbox, and
6
6
  * waits for people. See flows.ts for what each zone does.
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Schedules (v115: flow-owned; routines became scheduled flows).
3
+ *
4
+ * A schedule trigger fires on one of four readable cadences. Its two failure modes are subtle enough to deserve
5
+ * names: advancing one interval after downtime leaves the next firing in the past — a catch-up burst; advancing to
6
+ * `now + interval` drifts the cadence forever. The rule is ALIGNED advancement: the next occurrence strictly after
7
+ * now, anchored to the occurrence that just fired (for `every`) or to the clock itself (for calendar schedules).
8
+ * Missed slots while nothing was running fire ONCE — the overdue occurrence — and are never backfilled.
9
+ */
10
+ import type { StandingOrder } from "./contracts/flow.js";
11
+ import { type PhaseRoute } from "./phase-routing.js";
12
+ import { type AcceptanceCriterion, type ExecutionProfile } from "./scope.js";
13
+ import { type Store } from "./store.js";
14
+ export type { StandingOrder };
15
+ export type Schedule = {
16
+ kind: "every";
17
+ minutes: number;
18
+ } | {
19
+ kind: "daily";
20
+ hhmm: string;
21
+ timezone?: string;
22
+ }
23
+ /** v96: Monday to Friday. */
24
+ | {
25
+ kind: "weekdays";
26
+ hhmm: string;
27
+ timezone?: string;
28
+ } | {
29
+ kind: "weekly";
30
+ day: number;
31
+ hhmm: string;
32
+ timezone?: string;
33
+ };
34
+ export declare const WEEKDAYS: readonly ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
35
+ export declare function validTimezone(value: string): boolean;
36
+ /** Bounds a person would pick on purpose: 5 minutes to 7 days. */
37
+ export declare const MIN_EVERY_MINUTES = 5;
38
+ export declare const MAX_EVERY_MINUTES: number;
39
+ /**
40
+ * Existing daily schedules remain UTC. Calendar schedules may name an IANA
41
+ * timezone, whose wall-clock time stays fixed across daylight saving changes.
42
+ * No cron expressions in v1 — a schedule the operator cannot read at a
43
+ * glance is a schedule they cannot honestly approve.
44
+ */
45
+ export declare function parseSchedule(text: string): Schedule | null;
46
+ export declare function scheduleText(schedule: Schedule): string;
47
+ /** The schedule, in words an operator agrees to. */
48
+ export declare function describeSchedule(schedule: Schedule): string;
49
+ /**
50
+ * The first occurrence after approval: the schedule starts counting from the
51
+ * yes, because "approved at 14:07, every 60 minutes" firing instantly would
52
+ * spend before the approver's hand left the keyboard.
53
+ */
54
+ export declare function firstFireAt(schedule: Schedule, now: Date): string;
55
+ /**
56
+ * Aligned advancement: the smallest cadence occurrence STRICTLY after now.
57
+ * `anchor` is the occurrence that just fired (or was just skipped) — the
58
+ * cadence grid grows from it, so a pass that ran late does not tilt every
59
+ * later firing by its lateness.
60
+ */
61
+ export declare function nextFireAt(schedule: Schedule, anchorIso: string, now: Date): string;
62
+ /** The rolling window a standing order's cost ceiling counts over: seven days, and every surface says so. */
63
+ export declare const BUDGET_WINDOW_MS: number;
64
+ /** A firing's task id: the order's stem stamped with its slot (`nightly-deps-20261008-0700`). */
65
+ export declare function instanceId(stem: string, slotIso: string): string;
66
+ /**
67
+ * The exact standing order an approver agreed to, as 128 bits — byte for byte what a routine's digest was, so a
68
+ * routine's approval moved onto a scheduled flow (v115) still verifies: the project, the terms, the schedule, the
69
+ * ceiling, and the agents the approval froze. Editing any of them (or moving the flow) strands the approval.
70
+ */
71
+ export declare function standingDigestOf(repo: string, schedule: string, order: StandingOrder, acceptance: readonly AcceptanceCriterion[], profile: ExecutionProfile, route: PhaseRoute): string;
72
+ export type StandingApproval = {
73
+ live: true;
74
+ by: string | null;
75
+ profile: ExecutionProfile;
76
+ route: PhaseRoute;
77
+ acceptance: AcceptanceCriterion[];
78
+ } | {
79
+ live: false;
80
+ problem: string | null;
81
+ acceptance: AcceptanceCriterion[] | null;
82
+ };
83
+ /**
84
+ * Whether a standing order's carried approval still stands, read whole before anything is written: the frozen agents
85
+ * read back, re-hash with the order's terms, this schedule and project to the digest the approver signed, state no
86
+ * problem, and build and repair on the approved profile's own agents. `problem` is null when there is no approval.
87
+ */
88
+ export declare function standingApproval(order: StandingOrder, repo: string, schedule: string): StandingApproval;
89
+ export type StandingFiring = {
90
+ made: "filed";
91
+ taskId: string;
92
+ approved: boolean;
93
+ note: string | null;
94
+ } | {
95
+ made: "skipped";
96
+ note: string;
97
+ };
98
+ /** A firing that must not stand: thrown inside the transaction so nothing it wrote survives. */
99
+ export declare class StandingOrderRefused extends Error {
100
+ }
101
+ /**
102
+ * File one firing of a standing order (v115, what firing a routine did), inside the caller's transaction. It skips,
103
+ * saying why, while the last task is unfinished or the rolling 7-day ceiling is reached (an unmeasured paid run fails
104
+ * closed). Otherwise it files the order's terms unchanged: approved by the carried approval when that still verifies
105
+ * — on exactly the agents it froze, sealed like any automatic approval under the project's rules — or else as an
106
+ * ordinary proposal that waits for the project's approval rules like any other.
107
+ */
108
+ export declare function fileStandingOrder(store: Store, input: {
109
+ flowId: number;
110
+ repo: string;
111
+ trigger: number;
112
+ schedule: string;
113
+ order: StandingOrder;
114
+ title: string;
115
+ slot: string;
116
+ }, now: Date): StandingFiring;
117
+ /** A standing order in the words a person reads before turning its schedule on. */
118
+ export declare function describeStandingOrder(order: StandingOrder, repo: string, schedule: string): string[];
119
+ /** The terms a person sets a standing order up with (a template's, a recipe's): what each firing files. */
120
+ export type StandingTerms = {
121
+ goal: string;
122
+ outOfScope: string | null;
123
+ touches: string[];
124
+ requirements: string[];
125
+ acceptance: readonly AcceptanceCriterion[];
126
+ budgetPerRunMicrousd: number | null;
127
+ costCeilingUsd: number | null;
128
+ };
129
+ /** Every problem with a standing order's terms and schedule at once, as `field: problem` lines (none when it can be set up). */
130
+ export declare function standingTermsProblems(terms: StandingTerms, schedule: string): string[];
131
+ export type ScheduledFlowMade = {
132
+ ok: true;
133
+ flow: number;
134
+ trigger: number;
135
+ } | {
136
+ ok: false;
137
+ message: string;
138
+ };
139
+ /**
140
+ * A scheduled flow a person set up (a template or a recipe; v115, what filing a routine was): a Build step that follows
141
+ * the task each firing files, then Done, and a schedule trigger carrying the terms as its standing order — with no
142
+ * approval, so each firing waits for approval under the project's rules. The schedule starts PAUSED with no time:
143
+ * nothing repeats until a person turns it on, and it starts counting then. Its tasks are filed as `filedBy`'s (default
144
+ * `by`; null files them as automation's). Inside the caller's transaction, if any.
145
+ */
146
+ export declare function createScheduledFlow(store: Store, input: {
147
+ repo: string;
148
+ name: string;
149
+ stem: string;
150
+ schedule: string;
151
+ terms: StandingTerms;
152
+ by: string;
153
+ filedBy?: string | null;
154
+ }, now: Date): ScheduledFlowMade;