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.
- package/CHANGELOG.md +16 -0
- package/README.md +55 -80
- package/dist/agentconfig.d.ts +11 -36
- package/dist/agentconfig.js +3 -62
- package/dist/approval-policy.d.ts +1 -1
- package/dist/approval-policy.js +1 -1
- package/dist/board.d.ts +3 -19
- package/dist/board.js +4 -42
- package/dist/browser/workspace.js +18 -18
- package/dist/browser-workspace.d.ts +2 -1
- package/dist/builder.d.ts +9 -40
- package/dist/builder.js +50 -257
- package/dist/chat-actions.js +3 -14
- package/dist/chat-ask.js +8 -8
- package/dist/chat-controls.d.ts +0 -4
- package/dist/chat-controls.js +0 -1
- package/dist/chat-core.d.ts +171 -0
- package/dist/chat-core.js +288 -0
- package/dist/chat-decide.d.ts +10 -8
- package/dist/chat-decide.js +27 -24
- package/dist/chat-delivery-state.d.ts +51 -75
- package/dist/chat-delivery-state.js +140 -170
- package/dist/chat-delivery.d.ts +21 -4
- package/dist/chat-delivery.js +313 -195
- package/dist/chat-flow.d.ts +29 -1
- package/dist/chat-flow.js +24 -50
- package/dist/chat-messages.d.ts +319 -0
- package/dist/chat-messages.js +841 -0
- package/dist/chat-migration.d.ts +30 -0
- package/dist/chat-migration.js +802 -0
- package/dist/chat-quiet.d.ts +3 -0
- package/dist/chat-quiet.js +7 -2
- package/dist/chat-task-actions.d.ts +1 -1
- package/dist/chat-task-actions.js +0 -2
- package/dist/claim.d.ts +79 -244
- package/dist/claim.js +688 -1023
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +10 -2
- package/dist/contracts/chat-content.d.ts +71 -2
- package/dist/contracts/chat-content.js +22 -3
- package/dist/contracts/chat-tables.d.ts +11 -4
- package/dist/contracts/chat-tables.js +165 -87
- package/dist/contracts/cli.d.ts +0 -13
- package/dist/contracts/cli.js +0 -2
- package/dist/contracts/console-api.d.ts +5 -10
- package/dist/contracts/console-api.js +4 -10
- package/dist/contracts/discord-callback.js +2 -1
- package/dist/contracts/flow.d.ts +50 -0
- package/dist/contracts/flow.js +26 -1
- package/dist/contracts/lead-tools.d.ts +0 -13
- package/dist/contracts/lead-tools.js +3 -3
- package/dist/contracts/slack-callback.js +3 -2
- package/dist/contracts/store-json.d.ts +1 -29
- package/dist/contracts/store-json.js +4 -17
- package/dist/contracts/teams-callback.js +2 -1
- package/dist/converse.d.ts +1 -11
- package/dist/converse.js +6 -38
- package/dist/coordinator.d.ts +0 -1
- package/dist/coordinator.js +3 -5
- package/dist/demo.js +16 -26
- package/dist/desktop-update-gate.js +0 -1
- package/dist/desktop-update.js +1 -3
- package/dist/discord-chat.js +19 -38
- package/dist/discord-settings.js +4 -13
- package/dist/discord.js +4 -14
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +1 -1
- package/dist/dispose.d.ts +2 -3
- package/dist/dispose.js +17 -92
- package/dist/envelope.js +0 -3
- package/dist/exec.d.ts +0 -64
- package/dist/exec.js +3 -356
- package/dist/exhaustion.d.ts +22 -76
- package/dist/exhaustion.js +14 -140
- package/dist/export.js +8 -8
- package/dist/flow-engine.js +1 -1
- package/dist/flow-schedule.d.ts +154 -0
- package/dist/flow-schedule.js +321 -0
- package/dist/flow-share.js +1 -1
- package/dist/flow-triggers.d.ts +6 -1
- package/dist/flow-triggers.js +65 -10
- package/dist/flows-cli.js +8 -4
- package/dist/flows-ui.js +2 -0
- package/dist/flows.js +1 -1
- package/dist/guides.js +9 -29
- package/dist/invoke.d.ts +1 -24
- package/dist/invoke.js +17 -259
- package/dist/lead-status.js +2 -4
- package/dist/lead-voice.js +1 -1
- package/dist/mate-doors.d.ts +1 -2
- package/dist/mate-doors.js +12 -25
- package/dist/mate-tools.d.ts +2 -2
- package/dist/mate-tools.js +13 -24
- package/dist/modes.d.ts +19 -8
- package/dist/modes.js +24 -14
- package/dist/notification-rows.d.ts +30 -0
- package/dist/notification-rows.js +102 -0
- package/dist/operate-remote-arguments.js +2 -6
- package/dist/operate-remote.js +6 -7
- package/dist/operate.d.ts +0 -6
- package/dist/operate.js +226 -2248
- package/dist/phase-routing.d.ts +13 -23
- package/dist/phase-routing.js +13 -35
- package/dist/plan-auto.js +2 -2
- package/dist/plane-review.js +5 -11
- package/dist/planner-source.js +2 -1
- package/dist/policy.d.ts +1 -4
- package/dist/policy.js +0 -8
- package/dist/process-recovery-eligibility.js +2 -3
- package/dist/project-delete.js +24 -41
- package/dist/proposal.d.ts +4 -24
- package/dist/proposal.js +3 -88
- package/dist/provider-auth.d.ts +3 -3
- package/dist/provider-auth.js +4 -6
- package/dist/provider.d.ts +11 -25
- package/dist/provider.js +4 -36
- package/dist/push.js +1 -1
- package/dist/recipe-ui.js +6 -6
- package/dist/recipes.d.ts +3 -3
- package/dist/recipes.js +27 -23
- package/dist/result-shots.js +2 -2
- package/dist/retention.js +1 -1
- package/dist/run-closeout.d.ts +1 -1
- package/dist/run-closeout.js +1 -4
- package/dist/scope.d.ts +11 -65
- package/dist/scope.js +17 -82
- package/dist/serve.js +15 -24
- package/dist/server/chat.js +7 -30
- package/dist/server/flows.js +11 -10
- package/dist/server/guards.js +0 -1
- package/dist/server/pages-assets.js +1 -1
- package/dist/server/pages.js +0 -2
- package/dist/server/people-tokens.js +0 -4
- package/dist/server/route-table.d.ts +1 -1
- package/dist/server/route-table.js +2 -17
- package/dist/server/settings.js +6 -12
- package/dist/server/shared.d.ts +28 -212
- package/dist/server/shared.js +71 -780
- package/dist/server/task-views.js +9 -76
- package/dist/server/tasks.d.ts +1 -50
- package/dist/server/tasks.js +36 -946
- package/dist/setup-guide.d.ts +2 -2
- package/dist/slack-api.d.ts +5 -1
- package/dist/slack-api.js +2 -2
- package/dist/slack-chat.d.ts +9 -6
- package/dist/slack-chat.js +29 -53
- package/dist/slack-settings.js +6 -15
- package/dist/slack.js +6 -16
- package/dist/store.d.ts +200 -1702
- package/dist/store.js +811 -5439
- package/dist/surface.js +10 -24
- package/dist/task-composer.d.ts +1 -1
- package/dist/task-composer.js +1 -1
- package/dist/task-control.d.ts +3 -10
- package/dist/task-control.js +2 -7
- package/dist/teammate-desk.js +1 -1
- package/dist/teammate-question.js +12 -12
- package/dist/teammates-ui.js +1 -1
- package/dist/teams-chat.js +12 -9
- package/dist/teams-settings.js +2 -2
- package/dist/teams.js +4 -5
- package/dist/telegram-flow.js +5 -5
- package/dist/telegram-mate.d.ts +0 -3
- package/dist/telegram-mate.js +0 -1
- package/dist/telegram.js +4 -4
- package/dist/templates.d.ts +11 -7
- package/dist/templates.js +12 -9
- package/dist/toolroll-update.d.ts +20 -0
- package/dist/toolroll-update.js +104 -5
- package/dist/work-index.js +2 -6
- package/dist/workspace-revision.js +3 -1
- package/dist/workspace-ui.js +1 -1
- package/package.json +1 -1
- package/scripts/recovery-canary.mjs +3 -3
- package/dist/contest.d.ts +0 -318
- package/dist/contest.js +0 -753
- package/dist/held.d.ts +0 -150
- package/dist/held.js +0 -799
- package/dist/liveness.d.ts +0 -30
- package/dist/liveness.js +0 -42
- package/dist/pricing.d.ts +0 -45
- package/dist/pricing.js +0 -77
- package/dist/routine.d.ts +0 -228
- package/dist/routine.js +0 -764
- package/dist/slack-state.d.ts +0 -13
- package/dist/slack-state.js +0 -10
- package/dist/supervisor.mjs +0 -297
package/dist/phase-routing.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Explainable
|
|
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
|
-
*
|
|
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
|
|
39
|
-
*
|
|
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
|
|
82
|
-
*
|
|
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
|
|
312
|
-
*
|
|
313
|
-
* entry
|
|
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,
|
package/dist/phase-routing.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Explainable
|
|
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
|
-
*
|
|
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
|
|
39
|
-
*
|
|
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
|
|
89
|
-
*
|
|
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
|
-
|
|
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:
|
|
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
|
-
//
|
|
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.
|
|
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",
|
|
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);
|
package/dist/plane-review.js
CHANGED
|
@@ -175,17 +175,11 @@ export function reviewPlane(store, now, canSee = () => true) {
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
// Chat replies that couldn't be delivered.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
add(`chat/${app}`, `${name} replies weren't delivered`, ["reply", "replies"], { evidence: `After ${plural(Number(row["attempts"] ?? 0), "try", "tries")}: ${excerpt(str(row["problem"])) || "dropped"}` });
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
if (tableExists(store, "telegram_conversation_part")) {
|
|
187
|
-
for (const row of store.handle.prepare("SELECT last_error, attempts FROM telegram_conversation_part WHERE state = 'dropped' AND created_at >= ?").all(since)) {
|
|
188
|
-
add("chat/telegram", "Telegram replies weren't delivered", ["reply", "replies"], { evidence: `After ${plural(Number(row["attempts"] ?? 0), "try", "tries")}: ${excerpt(str(row["last_error"])) || "dropped"}` });
|
|
178
|
+
if (tableExists(store, "chat_part")) {
|
|
179
|
+
const names = { telegram: "Telegram", slack: "Slack", discord: "Discord", teams: "Teams" };
|
|
180
|
+
for (const row of store.handle.prepare("SELECT provider, problem, attempts FROM chat_part WHERE state = 'dropped' AND created >= ? ORDER BY provider = 'telegram' DESC, provider, id").all(since)) {
|
|
181
|
+
const app = String(row["provider"]);
|
|
182
|
+
add(`chat/${app}`, `${names[app] ?? app} replies weren't delivered`, ["reply", "replies"], { evidence: `After ${plural(Number(row["attempts"] ?? 0), "try", "tries")}: ${excerpt(str(row["problem"])) || "dropped"}` });
|
|
189
183
|
}
|
|
190
184
|
}
|
|
191
185
|
if (tableExists(store, "notification_delivery")) {
|
package/dist/planner-source.js
CHANGED
|
@@ -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
|
-
|
|
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),
|
|
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 ||
|
|
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 });
|
package/dist/project-delete.js
CHANGED
|
@@ -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
|
-
["
|
|
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
|
-
...(
|
|
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);
|
|
@@ -209,35 +207,30 @@ function deleteRows(store, repo, d, now) {
|
|
|
209
207
|
const notifications = json(ids(db, `SELECT id FROM notification WHERE project = ? OR ${IN("task_ref")} OR ${IN("source_run")}`, repo, T, R));
|
|
210
208
|
const questions = json(ids(db, `SELECT id FROM teammate_question WHERE ${IN("teammate")} OR ${IN("card")}`, M, C));
|
|
211
209
|
const proposals = json(ids(db, `SELECT id FROM mate_proposal WHERE ${IN("thread")}`, H));
|
|
212
|
-
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
del("
|
|
226
|
-
del("
|
|
227
|
-
del("
|
|
228
|
-
del("
|
|
229
|
-
del("
|
|
230
|
-
del("telegram_flow_prompt", IN("card"), C);
|
|
231
|
-
del("telegram_flow_choice", IN("card"), C);
|
|
210
|
+
// A chat message about one of the project's tasks (its turn named it), with its reply. Event ids are text ('m123'),
|
|
211
|
+
// and the same id can be another app's: each is kept whole, as provider:id.
|
|
212
|
+
const conversations = json(list(db, `SELECT provider || ':' || id FROM chat_event WHERE kind = 'message' AND json_extract(payload, '$.about.task') IN (SELECT value FROM json_each(?))`, K));
|
|
213
|
+
// Every app's planned parts about the project's work: a result screenshot, or a message naming one of its tasks or runs.
|
|
214
|
+
const parts = `SELECT provider || ':' || id FROM chat_part WHERE provider || ':' || event IN (SELECT value FROM json_each(?))
|
|
215
|
+
OR json_extract(payload, '$.image.taskId') IN (SELECT value FROM json_each(?)) OR json_extract(payload, '$.image.run') IN (SELECT value FROM json_each(?))`;
|
|
216
|
+
const doomedParts = `provider || ':' || part IN (${parts})`;
|
|
217
|
+
// Chats about the project's work, on every app.
|
|
218
|
+
del("chat_progress", IN("run"), R);
|
|
219
|
+
del("chat_action", `${IN("proposal")} OR ${IN("decision")} OR ${doomedParts}`, proposals, decisions, conversations, K, R);
|
|
220
|
+
del("chat_flow_action", `${IN("card")} OR ${doomedParts}`, C, conversations, K, R);
|
|
221
|
+
del("chat_flow_prompt", IN("card"), C);
|
|
222
|
+
del("chat_flow_choice", `${IN("card")} OR ${doomedParts}`, C, conversations, K, R);
|
|
223
|
+
del("chat_flow_note", IN("card"), C);
|
|
224
|
+
del("chat_question_action", `${IN("question")} OR ${doomedParts}`, questions, conversations, K, R);
|
|
225
|
+
del("chat_question_prompt", IN("question"), questions);
|
|
226
|
+
del("chat_ask_action", `${IN("turn")} OR ${doomedParts}`, U, conversations, K, R);
|
|
227
|
+
del("chat_note_draft", IN("decision"), decisions);
|
|
232
228
|
del("flow_send", IN("card"), C);
|
|
233
|
-
del("telegram_question_action", IN("question"), questions);
|
|
234
|
-
del("telegram_question_prompt", IN("question"), questions);
|
|
235
229
|
del("chat_decide_action", IN("task_id"), K);
|
|
236
230
|
del("chat_decide_prompt", IN("task_id"), K);
|
|
237
|
-
del("
|
|
238
|
-
del("
|
|
239
|
-
del("
|
|
240
|
-
del("telegram_outbound_message", `project = ? OR ${IN("notification")} OR ${IN("task_ref")} OR ${IN("source_run")}`, repo, notifications, T, R);
|
|
231
|
+
del("chat_message_ref", `${IN("decision")} OR ${IN("task_id")} OR ${IN("run")} OR project = ? OR ${IN("notification")} OR ${IN("task_ref")}`, decisions, K, R, repo, notifications, T);
|
|
232
|
+
del("chat_part", `provider || ':' || id IN (${parts})`, conversations, K, R);
|
|
233
|
+
del("chat_event", `provider || ':' || id IN (SELECT value FROM json_each(?))`, conversations);
|
|
241
234
|
del("chat_card_task", IN("task_ref"), T);
|
|
242
235
|
del("chat_batch_item", IN("task_ref"), T);
|
|
243
236
|
del("result_shot_sent", IN("run"), R);
|
|
@@ -263,7 +256,7 @@ function deleteRows(store, repo, d, now) {
|
|
|
263
256
|
del("diff_comment", `${IN("run")} OR ${IN("reviewer_run")} OR ${IN("artifact")}`, R, R, A);
|
|
264
257
|
del("review_request", `${IN("run")} OR ${IN("reviewer_run")}`, R, R);
|
|
265
258
|
del("repair_chain", IN("source_run"), R);
|
|
266
|
-
for (const table of ["incident", "
|
|
259
|
+
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
260
|
del(table, IN("run"), R);
|
|
268
261
|
del("knowledge_snapshot", `repo = ? OR ${IN("run")}`, repo, R);
|
|
269
262
|
del("learning_snapshot", `repo = ? OR ${IN("run")}`, repo, R);
|
|
@@ -277,19 +270,10 @@ function deleteRows(store, repo, d, now) {
|
|
|
277
270
|
del("publication", IN("id"), publications);
|
|
278
271
|
del("artifact", IN("id"), A);
|
|
279
272
|
// 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
273
|
del("run", IN("id"), R);
|
|
289
274
|
del("plan_revision", IN("task_ref"), T);
|
|
290
275
|
for (const table of ["claim", "hold", "task_steer"])
|
|
291
276
|
del(table, IN("task_ref"), T);
|
|
292
|
-
del("routine_fire", IN("instance_task_ref"), T);
|
|
293
277
|
const mirrors = json(list(db, `SELECT local_task_id FROM external_mirror WHERE ${IN("local_task_id")}`, K));
|
|
294
278
|
del("external_intent", IN("mirror"), mirrors);
|
|
295
279
|
del("external_mirror", IN("local_task_id"), mirrors);
|
|
@@ -323,7 +307,6 @@ function deleteRows(store, repo, d, now) {
|
|
|
323
307
|
del("operating_mode_event", "mode IN (SELECT id FROM operating_mode WHERE repo = ?)", repo);
|
|
324
308
|
del("decision_change", "decision IN (SELECT id FROM project_decision WHERE repo = ?)", repo);
|
|
325
309
|
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
310
|
for (const [table, column] of SETTINGS)
|
|
328
311
|
del(table, `${column} = ?`, repo);
|
|
329
312
|
del("project_mute", "repo = ?", repo);
|
package/dist/proposal.d.ts
CHANGED
|
@@ -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 {
|
|
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,
|
|
9
|
-
*
|
|
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
|
-
}
|