toolroll 0.9.51 → 0.9.53

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 (164) hide show
  1. package/CHANGELOG.md +20 -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/backup.d.ts +8 -0
  8. package/dist/backup.js +10 -1
  9. package/dist/board.d.ts +3 -19
  10. package/dist/board.js +4 -42
  11. package/dist/browser/workspace.css +1 -1
  12. package/dist/browser/workspace.js +18 -18
  13. package/dist/browser-workspace.d.ts +3 -1
  14. package/dist/builder.d.ts +9 -40
  15. package/dist/builder.js +50 -257
  16. package/dist/chat-actions.js +3 -14
  17. package/dist/chat-controls.d.ts +0 -4
  18. package/dist/chat-controls.js +0 -1
  19. package/dist/chat-decide.js +1 -3
  20. package/dist/chat-delivery.d.ts +0 -2
  21. package/dist/chat-delivery.js +0 -1
  22. package/dist/chat-quiet.js +2 -2
  23. package/dist/chat-task-actions.d.ts +1 -1
  24. package/dist/chat-task-actions.js +0 -2
  25. package/dist/claim.d.ts +79 -244
  26. package/dist/claim.js +688 -1023
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.js +10 -2
  29. package/dist/contracts/cli.d.ts +0 -13
  30. package/dist/contracts/cli.js +0 -2
  31. package/dist/contracts/console-api.d.ts +5 -10
  32. package/dist/contracts/console-api.js +4 -10
  33. package/dist/contracts/flow.d.ts +50 -0
  34. package/dist/contracts/flow.js +26 -1
  35. package/dist/contracts/lead-tools.d.ts +0 -13
  36. package/dist/contracts/lead-tools.js +3 -3
  37. package/dist/contracts/store-json.d.ts +1 -25
  38. package/dist/contracts/store-json.js +3 -13
  39. package/dist/converse.d.ts +1 -11
  40. package/dist/converse.js +6 -38
  41. package/dist/coordinator.d.ts +0 -1
  42. package/dist/coordinator.js +3 -5
  43. package/dist/database-reclaim.d.ts +4 -0
  44. package/dist/database-reclaim.js +13 -0
  45. package/dist/demo.d.ts +1 -1
  46. package/dist/demo.js +17 -27
  47. package/dist/desktop-update-gate.js +0 -1
  48. package/dist/desktop-update.d.ts +1 -1
  49. package/dist/desktop-update.js +1 -3
  50. package/dist/dispatch.d.ts +1 -1
  51. package/dist/dispatch.js +1 -1
  52. package/dist/dispose.d.ts +2 -3
  53. package/dist/dispose.js +17 -92
  54. package/dist/envelope.js +0 -3
  55. package/dist/exec.d.ts +0 -64
  56. package/dist/exec.js +3 -356
  57. package/dist/exhaustion.d.ts +22 -76
  58. package/dist/exhaustion.js +14 -140
  59. package/dist/export.js +4 -4
  60. package/dist/flow-engine.js +1 -1
  61. package/dist/flow-schedule.d.ts +154 -0
  62. package/dist/flow-schedule.js +321 -0
  63. package/dist/flow-share.js +1 -1
  64. package/dist/flow-triggers.d.ts +6 -1
  65. package/dist/flow-triggers.js +65 -10
  66. package/dist/flows-cli.js +8 -4
  67. package/dist/flows-ui.js +2 -0
  68. package/dist/flows.js +1 -1
  69. package/dist/guides.js +9 -29
  70. package/dist/invoke.d.ts +1 -24
  71. package/dist/invoke.js +17 -259
  72. package/dist/lead-status.js +2 -4
  73. package/dist/lead-voice.js +1 -1
  74. package/dist/live-bus.d.ts +3 -3
  75. package/dist/live-bus.js +3 -3
  76. package/dist/mate-doors.d.ts +1 -2
  77. package/dist/mate-doors.js +12 -25
  78. package/dist/mate-tools.d.ts +2 -2
  79. package/dist/mate-tools.js +10 -22
  80. package/dist/modes.d.ts +6 -7
  81. package/dist/modes.js +7 -13
  82. package/dist/monitoring.js +32 -8
  83. package/dist/notification-delivery.d.ts +19 -0
  84. package/dist/notification-delivery.js +37 -0
  85. package/dist/operate-remote-arguments.js +2 -6
  86. package/dist/operate-remote.js +6 -7
  87. package/dist/operate.d.ts +0 -6
  88. package/dist/operate.js +283 -2378
  89. package/dist/phase-routing.d.ts +13 -23
  90. package/dist/phase-routing.js +13 -35
  91. package/dist/plan-auto.js +2 -2
  92. package/dist/planner-source.js +2 -1
  93. package/dist/policy.d.ts +1 -4
  94. package/dist/policy.js +0 -8
  95. package/dist/process-recovery-eligibility.js +2 -3
  96. package/dist/process-recovery-settlement.js +1 -0
  97. package/dist/project-delete.js +3 -15
  98. package/dist/proposal.d.ts +4 -24
  99. package/dist/proposal.js +3 -88
  100. package/dist/provider-auth.d.ts +3 -3
  101. package/dist/provider-auth.js +4 -6
  102. package/dist/provider.d.ts +11 -25
  103. package/dist/provider.js +4 -36
  104. package/dist/push.js +1 -1
  105. package/dist/recipe-ui.js +6 -6
  106. package/dist/recipes.d.ts +3 -3
  107. package/dist/recipes.js +27 -23
  108. package/dist/retention.js +6 -5
  109. package/dist/run-closeout.d.ts +1 -1
  110. package/dist/run-closeout.js +1 -4
  111. package/dist/scope.d.ts +11 -65
  112. package/dist/scope.js +17 -82
  113. package/dist/serve.js +16 -25
  114. package/dist/server/chat.js +7 -30
  115. package/dist/server/flows.js +11 -10
  116. package/dist/server/guards.js +0 -1
  117. package/dist/server/pages-assets.js +1 -1
  118. package/dist/server/pages.js +0 -2
  119. package/dist/server/people-tokens.js +0 -4
  120. package/dist/server/route-table.d.ts +1 -1
  121. package/dist/server/route-table.js +2 -17
  122. package/dist/server/settings.js +4 -9
  123. package/dist/server/shared.d.ts +29 -212
  124. package/dist/server/shared.js +73 -780
  125. package/dist/server/task-views.js +9 -76
  126. package/dist/server/tasks.d.ts +1 -50
  127. package/dist/server/tasks.js +36 -946
  128. package/dist/setup-guide.d.ts +2 -2
  129. package/dist/slack-chat.d.ts +0 -2
  130. package/dist/slack-chat.js +0 -1
  131. package/dist/store.d.ts +142 -1382
  132. package/dist/store.js +900 -4083
  133. package/dist/surface.js +10 -25
  134. package/dist/task-composer.d.ts +1 -1
  135. package/dist/task-composer.js +1 -1
  136. package/dist/task-control.d.ts +3 -10
  137. package/dist/task-control.js +2 -7
  138. package/dist/teammate-desk.js +1 -1
  139. package/dist/teammates-ui.js +1 -1
  140. package/dist/telegram-mate.d.ts +0 -3
  141. package/dist/telegram-mate.js +0 -1
  142. package/dist/templates.d.ts +11 -7
  143. package/dist/templates.js +12 -9
  144. package/dist/toolroll-update.d.ts +24 -3
  145. package/dist/toolroll-update.js +159 -16
  146. package/dist/webhooks.d.ts +10 -22
  147. package/dist/webhooks.js +43 -81
  148. package/dist/work-index.js +3 -7
  149. package/dist/workspace-revision.d.ts +28 -1
  150. package/dist/workspace-revision.js +276 -49
  151. package/dist/workspace-ui.js +1 -1
  152. package/package.json +1 -3
  153. package/scripts/recovery-canary.mjs +3 -3
  154. package/dist/contest.d.ts +0 -318
  155. package/dist/contest.js +0 -753
  156. package/dist/held.d.ts +0 -150
  157. package/dist/held.js +0 -799
  158. package/dist/liveness.d.ts +0 -30
  159. package/dist/liveness.js +0 -42
  160. package/dist/pricing.d.ts +0 -45
  161. package/dist/pricing.js +0 -77
  162. package/dist/routine.d.ts +0 -228
  163. package/dist/routine.js +0 -764
  164. package/dist/supervisor.mjs +0 -297
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Explainable, risk-aware phase routing (v47).
2
+ * Explainable phase routing (v47), sized since v2.
3
3
  *
4
4
  * WHICH agent plans, builds, repairs, and reviews a task is decided here,
5
5
  * once, from signed facts — never guessed from a model's name and never
6
6
  * re-decided behind an approval's back. The policy is a small table over
7
7
  * explicit inputs:
8
8
  *
9
- * risk — the task's declared risk (routine / elevated / high)
9
+ * size — how big a change the task is (small / medium / large, risky or not)
10
10
  * quality — the evidence policy the scope signs (default / strict)
11
11
  * evidence — what the acceptance rubric demands (screenshots, …)
12
12
  * publication — how far the plane may carry the result unattended
@@ -35,8 +35,11 @@
35
35
  * is what actually runs. Provider readiness is a runner's observation,
36
36
  * not a term — it rides beside the route (ready / unavailable / unknown),
37
37
  * it is stated wherever the route is shown, and an unavailable provider
38
- * is never substituted: admission halts with the reason, and only an
39
- * already-approved explicit fallback entry may ever run instead.
38
+ * is never substituted: admission halts with the reason.
39
+ *
40
+ * Every route carries `risk: "routine"`: the declared risk level was
41
+ * removed in v115 (size does that job), and older sealed routes keep
42
+ * whatever risk they were recommended for, read back exactly.
40
43
  *
41
44
  * Approval seals the route (`approvedRouteJson`); its digest folds into
42
45
  * the scope digest of EVERY route filed since v47 — routine-shaped routes
@@ -69,8 +72,8 @@ export declare const ROUTE_ERA = 1;
69
72
  export declare const PHASES: readonly Phase[];
70
73
  /** The review leg remains readable in signed history but is no longer scheduled. */
71
74
  export declare const ACTIVE_PHASES: readonly Phase[];
75
+ /** The risk words a route sealed before v115 may carry (history); every route since says `routine`. */
72
76
  export type RiskLevel = (typeof RISKS)[number];
73
- export declare const RISK_LEVELS: readonly RiskLevel[];
74
77
  export declare function isRiskLevel(value: unknown): value is RiskLevel;
75
78
  /** How big a change the task is, sized once at filing (by the classifier,
76
79
  * by the description when it cannot answer, or by a person). */
@@ -78,26 +81,14 @@ export type TaskSize = TaskSizing["size"];
78
81
  export declare const TASK_SIZES: readonly TaskSize[];
79
82
  export type SizeSource = TaskSizing["source"];
80
83
  export declare function isTaskSize(value: unknown): value is TaskSize;
81
- /** Whether a task makes no plan: a small change that is not risky, at
82
- * routine risk. Elevated or high risk always plans, whatever the size. */
84
+ /** Whether a task makes no plan: a small change that is not risky (on an
85
+ * older route sealed at elevated or high risk, it always planned). */
83
86
  export declare function makesNoPlan(size: TaskSizing | null | undefined, risk: RiskLevel): boolean;
84
87
  /** Where a size came from, in words. */
85
88
  export declare function sizeSourceWords(source: SizeSource): string;
86
89
  /** Strict read of a stored sizing (the route contract's): exact keys, known words, bounded reason. */
87
90
  export declare function parseSizing(raw: unknown): TaskSizing | null;
88
91
  export declare function riskTitle(risk: RiskLevel): string;
89
- /**
90
- * What declaring each risk level DOES, in one plain sentence — derived
91
- * from the tiering table below, so the explanation a form or a chat gives
92
- * can never drift from the policy that acts on it. The same words on
93
- * every surface: the task page's risk control, the chat's answer, the CLI.
94
- */
95
- export declare function riskConsequence(risk: RiskLevel): string;
96
- export declare const RISK_CHOICES: readonly {
97
- risk: RiskLevel;
98
- title: string;
99
- consequence: string;
100
- }[];
101
92
  /** A runner's non-spending observation of one provider. `unknown` is a
102
93
  * real answer (claude has no login probe that does not spend) and is never
103
94
  * upgraded to ready. */
@@ -184,7 +175,6 @@ export declare function headroomFrom(limits: readonly {
184
175
  observedAt: string;
185
176
  }[], now: Date): ProviderRoom[];
186
177
  export type RouteInput = {
187
- risk: RiskLevel;
188
178
  qualityMode: QualityMode;
189
179
  evidence: readonly RouteEvidenceKind[];
190
180
  publication: PublicationAuthority;
@@ -308,9 +298,9 @@ export declare function routeWords(projection: RouteProjection, indent?: string)
308
298
  /** The empty readiness lookup: every provider unknown. */
309
299
  export declare const NO_READINESS: ReadinessLookup;
310
300
  /** How a run's provenance names the leg it spent as: the frozen leg's own
311
- * word, `legacy` for a run governed by a pre-routing approval, a contest
312
- * lane, or an attended session, and `fallback` for an approved fallback
313
- * entry (and every repair of one). */
301
+ * word, and `legacy` for a run governed by a pre-routing approval. Before
302
+ * v115, `legacy` also named a watched session's run, and `fallback` an
303
+ * approved fallback entry's; those rows still read back. */
314
304
  export type RouteChosen = RouteLeg["chosen"] | "legacy" | "fallback";
315
305
  export declare const ROUTE_CHOSEN: readonly RouteChosen[];
316
306
  /** One run's route provenance: the exact provider and model that ran,
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Explainable, risk-aware phase routing (v47).
2
+ * Explainable phase routing (v47), sized since v2.
3
3
  *
4
4
  * WHICH agent plans, builds, repairs, and reviews a task is decided here,
5
5
  * once, from signed facts — never guessed from a model's name and never
6
6
  * re-decided behind an approval's back. The policy is a small table over
7
7
  * explicit inputs:
8
8
  *
9
- * risk — the task's declared risk (routine / elevated / high)
9
+ * size — how big a change the task is (small / medium / large, risky or not)
10
10
  * quality — the evidence policy the scope signs (default / strict)
11
11
  * evidence — what the acceptance rubric demands (screenshots, …)
12
12
  * publication — how far the plane may carry the result unattended
@@ -35,8 +35,11 @@
35
35
  * is what actually runs. Provider readiness is a runner's observation,
36
36
  * not a term — it rides beside the route (ready / unavailable / unknown),
37
37
  * it is stated wherever the route is shown, and an unavailable provider
38
- * is never substituted: admission halts with the reason, and only an
39
- * already-approved explicit fallback entry may ever run instead.
38
+ * is never substituted: admission halts with the reason.
39
+ *
40
+ * Every route carries `risk: "routine"`: the declared risk level was
41
+ * removed in v115 (size does that job), and older sealed routes keep
42
+ * whatever risk they were recommended for, read back exactly.
40
43
  *
41
44
  * Approval seals the route (`approvedRouteJson`); its digest folds into
42
45
  * the scope digest of EVERY route filed since v47 — routine-shaped routes
@@ -77,7 +80,6 @@ export const ROUTE_ERA = 1;
77
80
  export const PHASES = ROUTE_PHASES;
78
81
  /** The review leg remains readable in signed history but is no longer scheduled. */
79
82
  export const ACTIVE_PHASES = ["plan", "build", "repair"];
80
- export const RISK_LEVELS = RISKS;
81
83
  export function isRiskLevel(value) {
82
84
  return value === "routine" || value === "elevated" || value === "high";
83
85
  }
@@ -85,8 +87,8 @@ export const TASK_SIZES = TASK_SIZE_WORDS;
85
87
  export function isTaskSize(value) {
86
88
  return value === "small" || value === "medium" || value === "large";
87
89
  }
88
- /** Whether a task makes no plan: a small change that is not risky, at
89
- * routine risk. Elevated or high risk always plans, whatever the size. */
90
+ /** Whether a task makes no plan: a small change that is not risky (on an
91
+ * older route sealed at elevated or high risk, it always planned). */
90
92
  export function makesNoPlan(size, risk) {
91
93
  return size != null && size.size === "small" && !size.risky && risk === "routine";
92
94
  }
@@ -102,20 +104,6 @@ export function parseSizing(raw) {
102
104
  export function riskTitle(risk) {
103
105
  return risk === "high" ? "High risk" : risk === "elevated" ? "Elevated risk" : "Routine";
104
106
  }
105
- /**
106
- * What declaring each risk level DOES, in one plain sentence — derived
107
- * from the tiering table below, so the explanation a form or a chat gives
108
- * can never drift from the policy that acts on it. The same words on
109
- * every surface: the task page's risk control, the chat's answer, the CLI.
110
- */
111
- export function riskConsequence(risk) {
112
- if (risk === "high")
113
- return "every active role — planner, builder, and repair — uses the strongest agent you have configured";
114
- if (risk === "elevated")
115
- return "planning and building use the strongest agent you have configured";
116
- return "every role uses the everyday configured agent unless the work itself asks for more (strict quality, screenshots, or a self-merging mode)";
117
- }
118
- export const RISK_CHOICES = RISK_LEVELS.map(risk => ({ risk, title: riskTitle(risk), consequence: riskConsequence(risk) }));
119
107
  const TIER_RANK = { light: 0, routine: 1, strong: 2 };
120
108
  /** Above these a provider has no headroom: even a candidate one tier
121
109
  * stronger on another provider takes the phase. Below them, a tier's
@@ -171,16 +159,6 @@ export function specWords(spec) {
171
159
  return `${spec.provider} · ${spec.model}`;
172
160
  }
173
161
  const DEMANDS = [
174
- {
175
- when: input => input.risk === "high",
176
- phases: ["plan", "build", "repair", "review"],
177
- reason: "risk is high — every role uses the strongest configured agent",
178
- },
179
- {
180
- when: input => input.risk === "elevated",
181
- phases: ["plan", "build", "repair", "review"],
182
- reason: "risk is elevated — planning and building use the strongest configured agent",
183
- },
184
162
  {
185
163
  when: input => input.size?.size === "large",
186
164
  phases: ["plan", "build", "repair"],
@@ -218,7 +196,8 @@ function demandedTier(input, phase) {
218
196
  }
219
197
  function economyReason(input, phase) {
220
198
  const facts = [
221
- `risk is ${input.risk}`,
199
+ // Said as it always was, so a route's words (and its digest) stay the same.
200
+ "risk is routine",
222
201
  `quality is ${input.qualityMode === "strict" ? "strict" : "default"}`,
223
202
  ...(input.publication === "notify" ? ["publication waits for a person"] : []),
224
203
  ];
@@ -242,7 +221,6 @@ export function recommendRoute(input) {
242
221
  .sort((a, b) => PHASES.indexOf(a.phase) - PHASES.indexOf(b.phase));
243
222
  const overrideFor = (phase) => overrides.find(one => one.phase === phase) ?? null;
244
223
  const legs = [];
245
- // Elevated or high risk lifts both legs through the table, so it never reaches the small branches.
246
224
  const small = input.size?.size === "small";
247
225
  const pick = (phase) => {
248
226
  const chosen = pickTier(phase);
@@ -405,7 +383,7 @@ export function recommendRoute(input) {
405
383
  }
406
384
  return {
407
385
  version: ROUTE_VERSION,
408
- risk: input.risk,
386
+ risk: "routine",
409
387
  qualityMode: input.qualityMode,
410
388
  publication: input.publication,
411
389
  evidence: [...new Set(input.evidence)].sort(),
@@ -616,7 +594,7 @@ export function sizeWords(route) {
616
594
  const model = build?.tier === "light" ? "fast model" : build?.tier === "strong" ? "strongest model" : "everyday model";
617
595
  if (makesNoPlan(route.size, route.risk))
618
596
  return `Small change: ${model}, no plan`;
619
- // Elevated or high risk plans a small change too.
597
+ // (A route sealed at elevated or high risk planned a small change too.)
620
598
  const label = route.size.size === "large" ? "Large change" : route.size.size === "small" ? (route.size.risky ? "Small but risky change" : "Small change") : route.size.risky ? "Risky change" : "Medium change";
621
599
  if (route.size.size !== "large" && !route.size.risky && route.risk === "routine")
622
600
  return build?.tier === "strong" ? "Medium change: strongest agents" : "Medium change: everyday agents";
package/dist/plan-auto.js CHANGED
@@ -12,7 +12,7 @@ export const PLAN_AUTO_SCHEMA = `CREATE TABLE IF NOT EXISTS plan_authorization (
12
12
  function plainPlan(store, taskId) {
13
13
  const ref = store.lookupRef(taskId);
14
14
  return ref !== null && ref.deliverable === "branch" && ref.coordinatorCid === null &&
15
- store.activeTournamentTerms(ref.id) === null && store.openAuthorizationFor(ref.id) === null && store.mirrorByTask(taskId) === null;
15
+ store.mirrorByTask(taskId) === null;
16
16
  }
17
17
  /** Join the console's authority-free proposal to the authenticated filing
18
18
  * road in one transaction. Planning defers the seal until verified ingestion. */
@@ -24,7 +24,7 @@ export function applyModeToNewFiling(store, taskId, actor, now) {
24
24
  return;
25
25
  const filed = propose(store, { taskId, goal: scope.goal, outOfScope: scope.outOfScope, touches: scope.touches, acceptance: scope.acceptance,
26
26
  budgetMicrousd: scope.budgetMicrousd ?? coverage.defaultBudgetMicrousd,
27
- qualityMode: scope.qualityMode ?? "default", riskLevel: scope.riskLevel ?? "routine",
27
+ qualityMode: scope.qualityMode ?? "default",
28
28
  ...(coverage.escalated ? { posture: "escalated" } : {}), now });
29
29
  if (ref.plan === "requested")
30
30
  authorizePlanUnderMode(store, taskId, actor, now);
@@ -128,7 +128,8 @@ export function plannerContractOf(store, taskId) {
128
128
  agentPin: { provider: ref.agentProvider, model: ref.agentModel },
129
129
  planPin: { provider: ref.planProvider, model: ref.planModel },
130
130
  deliverable: ref.deliverable,
131
- riskLevel: ref.riskLevel ?? null,
131
+ // The declared risk level was removed in v115; the field stays in the recorded shape.
132
+ riskLevel: null,
132
133
  qualityMode: ref.qualityMode ?? null,
133
134
  permissionMode: ref.permissionMode ?? null,
134
135
  },
package/dist/policy.d.ts CHANGED
@@ -15,8 +15,7 @@
15
15
  * (lowered), or, when there is none, doesn't run (refused); the words say which.
16
16
  *
17
17
  * Every road that admits work asks here: scope approval, the tick before a
18
- * run starts (and build() as the last look), fallback entries, race lanes,
19
- * attended sessions, the lead and project chats, teammates and flow steps.
18
+ * run starts (and build() as the last look), the lead and project chats, teammates and flow steps.
20
19
  */
21
20
  import type { Database } from "./store.js";
22
21
  import type { ExecutionProfile } from "./scope.js";
@@ -87,8 +86,6 @@ export declare function underCeiling(policy: OrgPolicy, profile: ExecutionProfil
87
86
  export declare function sessionCeilingRefusal(policy: OrgPolicy, provider: string, what: string): string | null;
88
87
  /** Everything the policy says about a profile before it runs: refused (words), or what runs and any lowering. */
89
88
  export declare function profileVerdict(policy: OrgPolicy, profile: ExecutionProfile): CeilingVerdict;
90
- /** An attended session runs at exactly what the person signed, so nothing is lowered: it runs as signed, or is refused. */
91
- export declare function attendedRefusal(policy: OrgPolicy, profile: ExecutionProfile): string | null;
92
89
  /** At approval, a person signs exact terms: anything above the ceiling is refused with words (a new filing is lowered). */
93
90
  export declare function approvalRefusal(policy: OrgPolicy, profiles: readonly ExecutionProfile[], legs?: readonly {
94
91
  provider: string;
package/dist/policy.js CHANGED
@@ -166,14 +166,6 @@ export function profileVerdict(policy, profile) {
166
166
  return { ok: false, message: `Its repairs: ${repair.charAt(0).toLowerCase()}${repair.slice(1)}` };
167
167
  return underCeiling(policy, profile);
168
168
  }
169
- /** An attended session runs at exactly what the person signed, so nothing is lowered: it runs as signed, or is refused. */
170
- export function attendedRefusal(policy, profile) {
171
- const verdict = profileVerdict(policy, profile);
172
- if (!verdict.ok)
173
- return verdict.message;
174
- return verdict.lowered === null ? null
175
- : `This session asks for ${levelWords(profile)}, above the organisation policy's permission ceiling (${LEVEL_NAMES[policy.ceiling]}). Authorise it again with a lower permission, or ${WHERE.charAt(0).toLowerCase()}${WHERE.slice(1)}`;
176
- }
177
169
  /** At approval, a person signs exact terms: anything above the ceiling is refused with words (a new filing is lowered). */
178
170
  export function approvalRefusal(policy, profiles, legs = []) {
179
171
  for (const profile of profiles) {
@@ -46,7 +46,7 @@ export function preparedCandidateObserverGapEligibility(store, input) {
46
46
  !instant(run.startedAt) || !instant(run.finishedAt) || Date.parse(run.finishedAt) < Date.parse(run.startedAt) ||
47
47
  Date.parse(run.finishedAt) > now.getTime())
48
48
  return refuse("not-terminal-prepared-builder");
49
- if (run.providerStartedAt !== null || run.sessionId !== null || run.attendedAuthorization != null || run.contestant !== null ||
49
+ if (run.providerStartedAt !== null || run.sessionId !== null ||
50
50
  !commit(run.baseRevision) || !commit(run.headRevision) || !run.branch || !run.worktree)
51
51
  return refuse("provider-or-run-identity-unproven");
52
52
  const ref = store.refById(run.taskRef);
@@ -79,8 +79,7 @@ export function preparedCandidateObserverGapEligibility(store, input) {
79
79
  if (!one || one.outcome === null || !instant(one.startedAt) || !instant(one.finishedAt) ||
80
80
  Date.parse(one.finishedAt) < Date.parse(one.startedAt) || Date.parse(one.finishedAt) > Date.parse(run.finishedAt) ||
81
81
  one.providerStartedAt !== null || one.sessionId !== null ||
82
- store.applicableStopFor(id) !== null || ownedProcessCount(runOwnerTag(store, id)) > 0 ||
83
- store.heldSessionOf(id)?.endedAt === null)
82
+ store.applicableStopFor(id) !== null || ownedProcessCount(runOwnerTag(store, id)) > 0)
84
83
  return refuse("owned-run-not-settled");
85
84
  ownedRuns.push({ id, taskRef: one.taskRef, leaseId: one.leaseId, parentRun: one.parentRun, role: one.role,
86
85
  outcome: one.outcome, startedAt: one.startedAt, finishedAt: one.finishedAt, providerStartedAt: one.providerStartedAt, sessionId: one.sessionId });
@@ -123,6 +123,7 @@ export async function recoverPreparedObserverGap(store, input) {
123
123
  const actionId = store.recordAction({ at: new Date().toISOString(), actor: "system", repo: current.binding.run.repo,
124
124
  taskId: current.binding.run.taskId, runId: receipt.targetRun, action: "process absence recovered",
125
125
  outcome: digest, source: "work" });
126
+ store.compactRunProcesses(new Date(), receipt.targetRun);
126
127
  return { ok: true, recorded: true, ...result, actionId, certificatePath };
127
128
  }));
128
129
  }
@@ -48,7 +48,7 @@ function doomed(db, repo) {
48
48
  const SETTINGS = [
49
49
  ["approval_policy", "repo"], ["verify_command", "repo"], ["worktree_setup", "repo"], ["backend_grant", "repo"], ["publication_grant", "repo"],
50
50
  ["intake_grant", "repo"], ["capability", "repo"], ["project_tool", "repo"], ["phase_config", "scope"], ["phase_tier_config", "scope"],
51
- ["fallback_config", "scope"], ["learning_policy", "repo"], ["operating_mode", "repo"], ["routine", "repo"], ["project_knowledge", "repo"],
51
+ ["learning_policy", "repo"], ["operating_mode", "repo"], ["project_knowledge", "repo"],
52
52
  ["project_decision", "repo"], ["flow_script", "repo"], ["workflow_recipe", "repo"], ["team_lead_project", "project"],
53
53
  ];
54
54
  /** What Toolroll holds for a project, counted. */
@@ -84,7 +84,6 @@ export function projectRunning(store, repo, now) {
84
84
  const at = now.toISOString();
85
85
  const count = (sql, ...params) => Number(db.prepare(sql).get(...params)?.["n"] ?? 0);
86
86
  const building = count(`SELECT COUNT(DISTINCT task_ref) AS n FROM claim WHERE released_at IS NULL AND expires_at > ? AND task_ref IN (SELECT value FROM json_each(?))`, at, json(d.refs));
87
- const sessions = count("SELECT COUNT(*) AS n FROM held_session WHERE ended_at IS NULL AND run IN (SELECT value FROM json_each(?))", json(d.runs));
88
87
  const slots = count("SELECT COUNT(*) AS n FROM execution_slot WHERE state IN ('reserved','running') AND run IN (SELECT value FROM json_each(?))", json(d.runs));
89
88
  const chats = count("SELECT COUNT(*) AS n FROM mate_turn WHERE state IN ('queued','running') AND id IN (SELECT value FROM json_each(?))", json(d.turns));
90
89
  const steps = count("SELECT COUNT(*) AS n FROM flow_step_run WHERE state = 'running' AND card IN (SELECT value FROM json_each(?))", json(d.cards));
@@ -93,7 +92,7 @@ export function projectRunning(store, repo, now) {
93
92
  const checkouts = count("SELECT COUNT(*) AS n FROM worktree WHERE repo = ? AND runner IS NOT NULL AND released_at IS NULL", repo);
94
93
  return [
95
94
  ...(building > 0 ? [`${plural(building, "task")} ${building === 1 ? "is" : "are"} being built`] : []),
96
- ...(sessions > 0 || slots > 0 ? ["an agent is working"] : []),
95
+ ...(slots > 0 ? ["an agent is working"] : []),
97
96
  ...(chats > 0 ? ["a chat is answering"] : []),
98
97
  ...(steps > 0 ? ["a flow step is running"] : []),
99
98
  ...(calls > 0 ? ["a teammate is working"] : []),
@@ -117,7 +116,6 @@ async function removeCheckoutsAndBranches(store, repo, d, git, poolRoot) {
117
116
  const named = [
118
117
  ...d.tasks.flatMap(id => OWN_BRANCHES.map(prefix => `${prefix}${id}`)), ...recorded.map(row => row.branch),
119
118
  ...list(db, "SELECT DISTINCT branch FROM run WHERE branch IS NOT NULL AND id IN (SELECT value FROM json_each(?))", json(d.runs)),
120
- ...list(db, "SELECT DISTINCT c.branch FROM contestant c JOIN contest k ON k.id = c.contest WHERE c.branch IS NOT NULL AND k.task_ref IN (SELECT value FROM json_each(?))", json(d.refs)),
121
119
  ...list(db, "SELECT DISTINCT head FROM publication WHERE head IS NOT NULL AND task_ref IN (SELECT value FROM json_each(?))", json(d.refs)),
122
120
  ].map(String).filter(isOwnBranch);
123
121
  const ours = new Set(named);
@@ -263,7 +261,7 @@ function deleteRows(store, repo, d, now) {
263
261
  del("diff_comment", `${IN("run")} OR ${IN("reviewer_run")} OR ${IN("artifact")}`, R, R, A);
264
262
  del("review_request", `${IN("run")} OR ${IN("reviewer_run")}`, R, R);
265
263
  del("repair_chain", IN("source_run"), R);
266
- for (const table of ["incident", "held_session", "proof_acceptance", "proof_verdict", "run_process", "run_route", "run_spend", "run_tool", "run_note", "run_stop", "run_checkpoint", "session_turn", "execution_slot"])
264
+ for (const table of ["incident", "proof_acceptance", "proof_verdict", "run_process", "run_route", "run_spend", "run_tool", "run_note", "run_stop", "run_checkpoint", "execution_slot"])
267
265
  del(table, IN("run"), R);
268
266
  del("knowledge_snapshot", `repo = ? OR ${IN("run")}`, repo, R);
269
267
  del("learning_snapshot", `repo = ? OR ${IN("run")}`, repo, R);
@@ -277,19 +275,10 @@ function deleteRows(store, repo, d, now) {
277
275
  del("publication", IN("id"), publications);
278
276
  del("artifact", IN("id"), A);
279
277
  // Tasks and their versions.
280
- const cycles = json(ids(db, `SELECT id FROM fallback_cycle WHERE ${IN("task_ref")}`, T));
281
- del("fallback_transition", `${IN("cycle")} OR ${IN("predecessor_run")} OR ${IN("consumed_by")}`, cycles, R, R);
282
- del("fallback_cycle", IN("id"), cycles);
283
- const contests = json(ids(db, `SELECT id FROM contest WHERE ${IN("task_ref")}`, T));
284
- del("contestant", IN("contest"), contests);
285
- del("contest", IN("id"), contests);
286
- del("tournament_terms", IN("task_ref"), T);
287
- del("attended_authorization", IN("task_ref"), T);
288
278
  del("run", IN("id"), R);
289
279
  del("plan_revision", IN("task_ref"), T);
290
280
  for (const table of ["claim", "hold", "task_steer"])
291
281
  del(table, IN("task_ref"), T);
292
- del("routine_fire", IN("instance_task_ref"), T);
293
282
  const mirrors = json(list(db, `SELECT local_task_id FROM external_mirror WHERE ${IN("local_task_id")}`, K));
294
283
  del("external_intent", IN("mirror"), mirrors);
295
284
  del("external_mirror", IN("local_task_id"), mirrors);
@@ -323,7 +312,6 @@ function deleteRows(store, repo, d, now) {
323
312
  del("operating_mode_event", "mode IN (SELECT id FROM operating_mode WHERE repo = ?)", repo);
324
313
  del("decision_change", "decision IN (SELECT id FROM project_decision WHERE repo = ?)", repo);
325
314
  del("memory_sighting", "gap IN (SELECT id FROM memory_gap WHERE repo = ?) OR session IN (SELECT id FROM memory_session WHERE repo = ?)", repo, repo);
326
- del("routine_fire", "routine_id IN (SELECT id FROM routine WHERE repo = ?)", repo);
327
315
  for (const [table, column] of SETTINGS)
328
316
  del(table, `${column} = ?`, repo);
329
317
  del("project_mute", "repo = ?", repo);
@@ -1,7 +1,7 @@
1
1
  import type { Filer } from "./approval-policy.js";
2
2
  export { validateTaskText } from "./task-text.js";
3
3
  import type { Store } from "./store.js";
4
- import { type UnattendedPermissionMode } from "./scope.js";
4
+ import type { UnattendedPermissionMode } from "./scope.js";
5
5
  import type { QualityMode } from "./quality.js";
6
6
  import type { TaskSizing } from "./phase-routing.js";
7
7
  export type ProposalRefusal = {
@@ -48,6 +48,9 @@ export type TaskProposalInput = {
48
48
  * which is the fail-closed reading of an empty ceiling (finding 4).
49
49
  */
50
50
  admittedRepos?: readonly string[];
51
+ /** A per-task dollar cap in micro-USD, bound into the scope's digest (a
52
+ * scheduled flow's standing order carries one, v115). */
53
+ budgetMicrousd?: number | null;
51
54
  /** v2 routing: the task's size when the caller already knows it (a
52
55
  * person's choice). Absent, filing sizes it from the description at
53
56
  * once and the process's classifier refines it within five seconds. */
@@ -59,32 +62,9 @@ export type TaskProposalInput = {
59
62
  * size (v2 routing), a small change is not planned and a large or risky
60
63
  * one is; a medium one keeps the description's signals. */
61
64
  export declare function shouldPlanTask(input: TaskProposalInput, sizing?: TaskSizing): boolean;
62
- export type RoutineProposalInput = {
63
- name: string;
64
- /** v102: who is making it (its instances are filed as theirs). */
65
- createdBy?: string | null;
66
- repo: string;
67
- goal: string;
68
- outOfScope: string | null;
69
- touches: string[];
70
- /** v39: the signed rubric every instance's scope copies forward. */
71
- acceptance: unknown;
72
- requirements: string[];
73
- schedule: string;
74
- costCeilingUsd: number | null;
75
- /** Per-instance dollar cap in micro-USD (v16); optional, digest-bound. */
76
- budgetPerRunMicrousd?: number | null;
77
- filedVia: string;
78
- admittedRepos?: readonly string[];
79
- };
80
65
  export declare function fileTaskProposal(store: Store, input: TaskProposalInput, now: Date): {
81
66
  ok: true;
82
67
  id: string;
83
68
  planning: boolean;
84
69
  sizing: TaskSizing;
85
70
  } | ProposalRefusal;
86
- export declare function fileRoutineProposal(store: Store, input: RoutineProposalInput, now: Date): {
87
- ok: true;
88
- id: number;
89
- digest: string;
90
- } | ProposalRefusal;
package/dist/proposal.js CHANGED
@@ -5,8 +5,8 @@ export { validateTaskText } from "./task-text.js";
5
5
  *
6
6
  * Before this module, each filing surface — CLI, console, intake — validated
7
7
  * what it happened to think of: createConsoleTask checked title and goal but
8
- * not disguised text, the routine command checked terms but nothing checked a
9
- * repo against the caller's ceiling, and nothing stamped who filed what. The
8
+ * not disguised text, nothing checked a repo against the caller's ceiling,
9
+ * and nothing stamped who filed what. The
10
10
  * review's ruling: one canonical service that every filer calls, with an
11
11
  * exact field allowlist, so a careless caller CANNOT spread a template or
12
12
  * model object into scope rows and smuggle approval metadata.
@@ -27,12 +27,8 @@ export { validateTaskText } from "./task-text.js";
27
27
  * Filing carries NO authority. Everything this module creates is unapproved
28
28
  * and stays inert until the operator's own ceremony says otherwise.
29
29
  */
30
- import { resolveRoutineAuthority } from "./agentconfig.js";
31
30
  import { resolve } from "node:path";
32
- import { hasForbiddenControls, hasDisguisedText } from "./decision.js";
33
31
  import { canonicalProject } from "./project.js";
34
- import { ROUTINE_NAME, routineDigestOf, validateRoutineTerms, } from "./routine.js";
35
- import { parseAcceptanceCriteria } from "./scope.js";
36
32
  import { heuristicSizing, planningSignals, refineFiledSizing } from "./task-sizing.js";
37
33
  /** Provenance tokens are part of the audit surface: lowercase, bounded,
38
34
  * nothing that could render as anything but itself. */
@@ -65,18 +61,6 @@ export function shouldPlanTask(input, sizing) {
65
61
  function refuse(reason, message) {
66
62
  return { ok: false, reason, message };
67
63
  }
68
- /** The fields an approver reads must BE what they appear to be: no control
69
- * characters anywhere, no invisible or direction-override text either. */
70
- function dishonest(text) {
71
- return hasForbiddenControls(text) || hasDisguisedText(text);
72
- }
73
- /** Byte caps beside the character caps (Codex v3 review, change 8): a
74
- * 2000-character goal of astral-plane text is 8000 bytes — model-authored
75
- * fields must bound STORAGE, not just what a screen shows. */
76
- const BYTE_CAPS = { title: 800, name: 200, text: 8_000, path: 800, requirement: 400 };
77
- function overBytes(text, cap) {
78
- return Buffer.byteLength(text, "utf8") > cap;
79
- }
80
64
  function checkRepo(given, admitted) {
81
65
  if (given === undefined || given === "") {
82
66
  // A ceiling names the ONLY repos this surface may file into. Filing
@@ -119,6 +103,7 @@ export function fileTaskProposal(store, input, now) {
119
103
  ...(input.goal === undefined ? {} : { goal: input.goal }),
120
104
  ...(input.permissionMode === undefined ? {} : { permissionMode: input.permissionMode }),
121
105
  ...(input.qualityMode === undefined ? {} : { qualityMode: input.qualityMode }),
106
+ ...(input.budgetMicrousd == null ? {} : { budgetMicrousd: input.budgetMicrousd }),
122
107
  outOfScope,
123
108
  touches,
124
109
  acceptance: input.acceptance,
@@ -153,73 +138,3 @@ export function fileTaskProposal(store, input, now) {
153
138
  }
154
139
  return { ok: true, id: made.id, planning, sizing };
155
140
  }
156
- export function fileRoutineProposal(store, input, now) {
157
- if (!FILED_VIA.test(input.filedVia)) {
158
- return refuse("bad-provenance", "filedVia is an audit token: lowercase letters, digits, dashes, colons");
159
- }
160
- const repo = checkRepo(input.repo, input.admittedRepos);
161
- if (!repo.ok)
162
- return repo;
163
- if (repo.repo === undefined)
164
- return refuse("bad-repo", "a routine needs the repository it runs in");
165
- const acceptanceParse = parseAcceptanceCriteria(input.acceptance);
166
- const terms = {
167
- repo: repo.repo,
168
- goal: input.goal,
169
- outOfScope: input.outOfScope,
170
- touches: input.touches,
171
- acceptance: acceptanceParse.criteria,
172
- requirements: input.requirements,
173
- schedule: input.schedule,
174
- // v1 routines run one instance at a time, period — hardcoded, not accepted.
175
- singleFlight: true,
176
- costCeilingUsd: input.costCeilingUsd,
177
- ...(input.budgetPerRunMicrousd == null ? {} : { budgetPerRunMicrousd: input.budgetPerRunMicrousd }),
178
- };
179
- // EVERY problem at once — an operator fixing a form deserves the whole
180
- // list, not one complaint per submission.
181
- const problems = validateRoutineTerms(terms);
182
- if (acceptanceParse.problems.length > 0) {
183
- problems.push({ field: "acceptance", problem: acceptanceParse.problems.map(p => p.message).join("; ") });
184
- }
185
- if (!ROUTINE_NAME.test(input.name)) {
186
- problems.unshift({ field: "name", problem: "lowercase letters, digits, and dashes — it becomes each instance's id" });
187
- }
188
- if (dishonest(input.goal) ||
189
- overBytes(input.goal, BYTE_CAPS.text) ||
190
- (input.outOfScope !== null && (dishonest(input.outOfScope) || overBytes(input.outOfScope, BYTE_CAPS.text)))) {
191
- problems.push({ field: "goal", problem: "no control or disguised text, bounded bytes" });
192
- }
193
- // The v3 review, change 8: touches and requirements are approver-read
194
- // text too — the door holds them to the same honesty everywhere.
195
- if (input.touches.some(one => dishonest(one) || overBytes(one, BYTE_CAPS.path))) {
196
- problems.push({ field: "touches", problem: "no control or disguised text, bounded bytes" });
197
- }
198
- if (input.requirements.some(one => dishonest(one) || overBytes(one, BYTE_CAPS.requirement))) {
199
- problems.push({ field: "requirements", problem: "no control or disguised text, bounded bytes" });
200
- }
201
- if (problems.length > 0) {
202
- const named = problems.map(one => `${one.field}: ${one.problem}`).join("; ");
203
- return refuse(problems.some(one => one.field === "name") ? "bad-name" : "bad-terms", named);
204
- }
205
- // v24 filing invariant, routine flavor — since v48 the whole four-role
206
- // ROUTE: resolve which agents plan, build, repair, and review a firing
207
- // once, here, from the configuration of this moment, and bind route and
208
- // profile into the digest a person will sign. Unresolved saves too
209
- // (finding 19) — approval then refuses until the routine is restated.
210
- const authority = resolveRoutineAuthority(store, repo.repo, terms.acceptance, now);
211
- const routineProfile = authority.ok ? authority.profile : null;
212
- const routineRoute = authority.ok ? authority.route : null;
213
- const created = store.createRoutine({
214
- name: input.name,
215
- ...terms,
216
- digest: routineDigestOf(terms, routineProfile, routineRoute),
217
- filedVia: input.filedVia,
218
- createdBy: input.createdBy ?? null,
219
- ...(routineProfile === null ? {} : { profile: routineProfile }),
220
- ...(routineRoute === null ? {} : { route: routineRoute }),
221
- }, now);
222
- if (!created.ok)
223
- return refuse("duplicate", `a routine named ${input.name} already exists`);
224
- return { ok: true, id: created.id, digest: routineDigestOf(terms, routineProfile, routineRoute) };
225
- }
@@ -45,7 +45,7 @@ export declare function authPauseOf(store: Store, provider: string): AuthPause |
45
45
  /**
46
46
  * SQL: whether the pause `pause` (a provider_auth_pause alias) is on one of
47
47
  * the task_ref `taskRef`'s current providers — its pins, its working profile,
48
- * route legs and fallback entries — so a task re-routed to another provider
48
+ * and route legs — so a task re-routed to another provider
49
49
  * stops saying it waits on the old one. A task that names none yet (an
50
50
  * unpinned planner, whose agent comes from configuration) keeps what the gate
51
51
  * found.
@@ -118,8 +118,8 @@ export type SignInGate = {
118
118
  };
119
119
  /**
120
120
  * The sign-in gate every road that starts an agent asks before it claims
121
- * anything: the tick's queue, and the roads beside it (fallback entries,
122
- * attended continuations, contest resumes). Waits when a provider is paused,
121
+ * anything: the tick's queue and the roads beside it.
122
+ * Waits when a provider is paused,
123
123
  * unless this is the pause's one trial. A waiting task is noted on its
124
124
  * task_ref, so the work index says exactly what the gate decided; a task let
125
125
  * through is un-noted. A road whose claim fails after a trial calls giveBack.
@@ -72,7 +72,7 @@ export function authPauseOf(store, provider) {
72
72
  /**
73
73
  * SQL: whether the pause `pause` (a provider_auth_pause alias) is on one of
74
74
  * the task_ref `taskRef`'s current providers — its pins, its working profile,
75
- * route legs and fallback entries — so a task re-routed to another provider
75
+ * and route legs — so a task re-routed to another provider
76
76
  * stops saying it waits on the old one. A task that names none yet (an
77
77
  * unpinned planner, whose agent comes from configuration) keeps what the gate
78
78
  * found.
@@ -83,9 +83,7 @@ export function pauseOnTaskProviders(pause, taskRef) {
83
83
  UNION ALL SELECT json_extract(ts.profile_json, '$.provider') FROM task_ref r JOIN task_scope ts ON ts.task_id = r.external_id
84
84
  WHERE r.id = ${taskRef} AND json_valid(ts.profile_json)
85
85
  UNION ALL SELECT json_extract(leg.value, '$.provider') FROM task_ref r JOIN task_scope ts ON ts.task_id = r.external_id,
86
- json_each(CASE WHEN json_valid(ts.proposed_route_json) THEN ts.proposed_route_json ELSE '{}' END, '$.legs') leg WHERE r.id = ${taskRef}
87
- UNION ALL SELECT json_extract(entry.value, '$.profile.provider') FROM task_ref r JOIN task_scope ts ON ts.task_id = r.external_id,
88
- json_each(CASE WHEN json_valid(ts.proposed_chain_json) THEN ts.proposed_chain_json ELSE '{}' END, '$.chain') entry WHERE r.id = ${taskRef}`;
86
+ json_each(CASE WHEN json_valid(ts.proposed_route_json) THEN ts.proposed_route_json ELSE '{}' END, '$.legs') leg WHERE r.id = ${taskRef}`;
89
87
  return `(${pause}.provider IN (SELECT provider FROM (${providers}) WHERE provider IS NOT NULL)
90
88
  OR NOT EXISTS (SELECT 1 FROM (${providers}) WHERE provider IS NOT NULL))`;
91
89
  }
@@ -227,8 +225,8 @@ export function giveBackAuthTrial(store, trial) {
227
225
  }
228
226
  /**
229
227
  * The sign-in gate every road that starts an agent asks before it claims
230
- * anything: the tick's queue, and the roads beside it (fallback entries,
231
- * attended continuations, contest resumes). Waits when a provider is paused,
228
+ * anything: the tick's queue and the roads beside it.
229
+ * Waits when a provider is paused,
232
230
  * unless this is the pause's one trial. A waiting task is noted on its
233
231
  * task_ref, so the work index says exactly what the gate decided; a task let
234
232
  * through is un-noted. A road whose claim fails after a trial calls giveBack.