toolroll 0.9.67 → 0.9.69
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 +70 -0
- package/dist/acp-client.js +1 -3
- package/dist/agent-board.d.ts +0 -1
- package/dist/agent-board.js +0 -1
- package/dist/agent-fence.d.ts +58 -0
- package/dist/agent-fence.js +214 -19
- package/dist/agentconfig.d.ts +1 -1
- package/dist/agentconfig.js +6 -2
- package/dist/batch-checks.js +14 -26
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +31 -29
- package/dist/builder.d.ts +9 -0
- package/dist/builder.js +30 -27
- package/dist/canonical-json.d.ts +14 -0
- package/dist/canonical-json.js +36 -0
- package/dist/chat-actions.js +1 -2
- package/dist/chat-admission.d.ts +19 -0
- package/dist/chat-admission.js +22 -0
- package/dist/chat-channel.js +1 -1
- package/dist/chat-delivery.js +0 -1
- package/dist/chat-follow.d.ts +17 -0
- package/dist/chat-follow.js +32 -0
- package/dist/chat-messages.js +0 -1
- package/dist/chat-settings-page.d.ts +23 -0
- package/dist/chat-settings-page.js +41 -0
- package/dist/check-access.d.ts +54 -0
- package/dist/check-access.js +80 -0
- package/dist/claim.js +3 -6
- package/dist/claude-print.d.ts +12 -0
- package/dist/claude-print.js +34 -0
- package/dist/cli-http.d.ts +0 -7
- package/dist/cli-http.js +0 -21
- package/dist/cli.d.ts +3 -10
- package/dist/cli.js +15 -24
- package/dist/coding-provider.js +2 -1
- package/dist/coding-workspace.js +1 -1
- package/dist/{team-cli.d.ts → connect-cli.d.ts} +5 -9
- package/dist/connect-cli.js +331 -0
- package/dist/containment.d.ts +0 -2
- package/dist/containment.js +2 -6
- package/dist/contracts/chat-actions.d.ts +0 -1
- package/dist/contracts/chat-content.d.ts +0 -4
- package/dist/contracts/chat-content.js +0 -2
- package/dist/contracts/chat-tables.d.ts +0 -1
- package/dist/contracts/cli.d.ts +0 -62
- package/dist/contracts/cli.js +0 -4
- package/dist/contracts/coding-workspace.js +1 -1
- package/dist/contracts/console-api.d.ts +1 -0
- package/dist/contracts/console-api.js +1 -0
- package/dist/contracts/contract.js +1 -1
- package/dist/contracts/flow-card.d.ts +0 -1
- package/dist/contracts/flow-propose.d.ts +0 -1
- package/dist/contracts/flow-send.js +1 -1
- package/dist/contracts/gateway-tools.d.ts +0 -1
- package/dist/contracts/integration-metadata.js +1 -1
- package/dist/contracts/lead-tools.d.ts +0 -1
- package/dist/contracts/memory-pass.d.ts +0 -1
- package/dist/contracts/memory-pass.js +1 -1
- package/dist/contracts/plan.js +1 -1
- package/dist/contracts/project-knowledge.js +1 -1
- package/dist/contracts/project-skills.d.ts +0 -1
- package/dist/contracts/proof.js +1 -1
- package/dist/contracts/recipes.js +1 -1
- package/dist/contracts/scope.js +1 -1
- package/dist/contracts/scout-report.js +1 -1
- package/dist/contracts/teams-callback.d.ts +0 -1
- package/dist/converse.d.ts +0 -6
- package/dist/converse.js +0 -19
- package/dist/daemon.js +3 -2
- package/dist/decision.d.ts +1 -3
- package/dist/deprecations.d.ts +12 -4
- package/dist/deprecations.js +27 -4
- package/dist/desktop-update-recovery.js +1 -1
- package/dist/desktop-update.js +3 -2
- package/dist/discord-chat.js +4 -15
- package/dist/discord-settings.d.ts +2 -6
- package/dist/discord-settings.js +10 -38
- package/dist/discord.js +12 -23
- package/dist/evidence.d.ts +0 -1
- package/dist/evidence.js +0 -3
- package/dist/exec.d.ts +0 -9
- package/dist/exec.js +3 -81
- package/dist/flow-actions.js +0 -3
- package/dist/flow-draft.d.ts +1 -3
- package/dist/flow-draft.js +15 -28
- package/dist/flow-scripts.d.ts +1 -8
- package/dist/flow-scripts.js +0 -13
- package/dist/flow-send.js +1 -1
- package/dist/flow-share.d.ts +0 -2
- package/dist/flow-share.js +1 -6
- package/dist/flow-steps.js +1 -1
- package/dist/flow-triggers.d.ts +0 -1
- package/dist/flow-triggers.js +1 -2
- package/dist/flow-unattended.js +1 -12
- package/dist/flows.js +1 -1
- package/dist/health-cli.d.ts +2 -2
- package/dist/health-cli.js +1 -1
- package/dist/invoke.d.ts +39 -5
- package/dist/invoke.js +181 -24
- package/dist/is-record.d.ts +2 -0
- package/dist/is-record.js +2 -0
- package/dist/lead-doors.d.ts +1 -5
- package/dist/lead-doors.js +0 -4
- package/dist/lead-tools.js +3 -6
- package/dist/live-detail.d.ts +0 -1
- package/dist/live-detail.js +0 -6
- package/dist/live-terminal.js +2 -1
- package/dist/live.d.ts +0 -1
- package/dist/live.js +1 -2
- package/dist/mcp-core.js +0 -2
- package/dist/memory-pass.js +1 -1
- package/dist/model-catalog.js +1 -1
- package/dist/modes.js +1 -12
- package/dist/operate.d.ts +4 -0
- package/dist/operate.js +456 -750
- package/dist/password-bearer.d.ts +0 -2
- package/dist/password-bearer.js +0 -4
- package/dist/peek-cli.js +2 -1
- package/dist/peek.d.ts +2 -26
- package/dist/peek.js +2 -168
- package/dist/phase-routing.d.ts +0 -4
- package/dist/phase-routing.js +2 -21
- package/dist/planner-source.d.ts +2 -1
- package/dist/planner-source.js +2 -12
- package/dist/planner.js +2 -14
- package/dist/process-tree.d.ts +0 -2
- package/dist/process-tree.js +0 -10
- package/dist/project-delete-ui.d.ts +16 -0
- package/dist/project-delete-ui.js +60 -1
- package/dist/project-delete.d.ts +0 -2
- package/dist/project-delete.js +1 -3
- package/dist/project-learning.d.ts +0 -14
- package/dist/project-skills.d.ts +0 -1
- package/dist/project-skills.js +0 -14
- package/dist/project-tools.js +2 -10
- package/dist/provider.d.ts +0 -17
- package/dist/provider.js +0 -26
- package/dist/pulls.d.ts +0 -2
- package/dist/pulls.js +0 -9
- package/dist/release-adapters.d.ts +3 -0
- package/dist/release-adapters.js +15 -5
- package/dist/remote-exec.d.ts +3 -2
- package/dist/remote-exec.js +3 -2
- package/dist/repos.d.ts +0 -5
- package/dist/repos.js +2 -10
- package/dist/repository-context.d.ts +0 -2
- package/dist/repository-context.js +0 -4
- package/dist/restart-certification.d.ts +0 -2
- package/dist/restart-certification.js +1 -5
- package/dist/result-follow-ups.d.ts +1 -4
- package/dist/result-follow-ups.js +11 -21
- package/dist/result-review.d.ts +0 -2
- package/dist/result-review.js +0 -2
- package/dist/retention.d.ts +0 -1
- package/dist/retention.js +0 -1
- package/dist/review-context.d.ts +0 -1
- package/dist/review-context.js +1 -12
- package/dist/reviewer.d.ts +0 -16
- package/dist/reviewer.js +0 -18
- package/dist/run-closeout.js +2 -1
- package/dist/scope.d.ts +9 -0
- package/dist/scope.js +13 -18
- package/dist/scout-browser.js +2 -1
- package/dist/serve.js +11 -2
- package/dist/server/console.css +5 -133
- package/dist/server/flows.js +2 -2
- package/dist/server/http.d.ts +4 -0
- package/dist/server/http.js +5 -0
- package/dist/server/pages.js +2 -2
- package/dist/server/people-tokens.js +3 -4
- package/dist/server/remote.js +1 -6
- package/dist/server/render-chat.d.ts +9 -2
- package/dist/server/render-chat.js +53 -7
- package/dist/server/render-results.d.ts +3 -6
- package/dist/server/render-results.js +4 -9
- package/dist/server/render-tasks.js +6 -6
- package/dist/server/route-table.d.ts +0 -4
- package/dist/server/route-table.js +1 -6
- package/dist/server/settings.js +60 -23
- package/dist/server/task-views.js +1 -1
- package/dist/server/tasks.js +5 -3
- package/dist/setup-guide.d.ts +2 -2
- package/dist/skills.d.ts +0 -6
- package/dist/skills.js +0 -8
- package/dist/slack-chat.d.ts +1 -7
- package/dist/slack-chat.js +5 -18
- package/dist/slack-settings.d.ts +2 -6
- package/dist/slack-settings.js +10 -38
- package/dist/slack.js +12 -23
- package/dist/sleep.d.ts +2 -0
- package/dist/sleep.js +2 -0
- package/dist/store.d.ts +29 -75
- package/dist/store.js +89 -180
- package/dist/structured-output.d.ts +0 -1
- package/dist/structured-output.js +0 -1
- package/dist/subagent-work.d.ts +0 -2
- package/dist/subagent-work.js +0 -2
- package/dist/subagents.d.ts +1 -3
- package/dist/subagents.js +20 -33
- package/dist/subscription-chat.d.ts +1 -3
- package/dist/subscription-chat.js +2 -3
- package/dist/surface.js +2 -1
- package/dist/task-sizing.d.ts +1 -4
- package/dist/task-sizing.js +3 -10
- package/dist/teams-chat.js +4 -11
- package/dist/teams.js +12 -19
- package/dist/telegram-lead.js +2 -2
- package/dist/telegram.js +1 -1
- package/dist/token-notices.d.ts +0 -11
- package/dist/token-notices.js +1 -5
- package/dist/tool-secret-store.d.ts +0 -3
- package/dist/tool-secret-store.js +2 -11
- package/dist/toolroll-update.d.ts +0 -2
- package/dist/toolroll-update.js +2 -2
- package/dist/verification-evidence.d.ts +0 -1
- package/dist/verification-evidence.js +0 -1
- package/dist/webhooks.d.ts +0 -1
- package/dist/webhooks.js +0 -1
- package/dist/workspace-ui.d.ts +0 -8
- package/dist/workspace-ui.js +1 -6
- package/package.json +1 -1
- package/dist/contracts/native-census.d.ts +0 -42
- package/dist/contracts/native-census.js +0 -36
- package/dist/process-recovery-anchor.d.ts +0 -75
- package/dist/process-recovery-anchor.js +0 -188
- package/dist/process-recovery-coalition.d.ts +0 -25
- package/dist/process-recovery-coalition.js +0 -105
- package/dist/process-recovery-eligibility.d.ts +0 -179
- package/dist/process-recovery-eligibility.js +0 -174
- package/dist/process-recovery-native.d.ts +0 -161
- package/dist/process-recovery-native.js +0 -595
- package/dist/process-recovery-provenance.d.ts +0 -163
- package/dist/process-recovery-provenance.js +0 -292
- package/dist/process-recovery-services.d.ts +0 -56
- package/dist/process-recovery-services.js +0 -191
- package/dist/process-recovery-settlement.d.ts +0 -32
- package/dist/process-recovery-settlement.js +0 -133
- package/dist/process-recovery.d.ts +0 -41
- package/dist/process-recovery.js +0 -91
- package/dist/session-brief.d.ts +0 -8
- package/dist/session-brief.js +0 -24
- package/dist/session-cli.d.ts +0 -42
- package/dist/session-cli.js +0 -271
- package/dist/session-contract.d.ts +0 -183
- package/dist/session-contract.js +0 -122
- package/dist/session-remote.d.ts +0 -19
- package/dist/session-remote.js +0 -86
- package/dist/session-service.d.ts +0 -22
- package/dist/session-service.js +0 -157
- package/dist/team-cli.js +0 -692
package/dist/store.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { WRITES_DEFAULT } from "./writes-mode.js";
|
|
2
|
-
import {
|
|
2
|
+
import { checkAccessOf, widerThan } from "./check-access.js";
|
|
3
|
+
import { networkFromValue, PROJECT_NETWORK_DEFAULT, projectNetworkFromValue, runNetworkFromValue } from "./egress.js";
|
|
3
4
|
import { isAcpProfile, isAcpProvider } from "./acp-agents.js";
|
|
4
5
|
import { ACP_CONFORMANCE_VERSION, launchFromJson } from "./acp-attest.js";
|
|
5
6
|
import { reclaimDatabase } from "./database-reclaim.js";
|
|
@@ -68,13 +69,13 @@ import { containerEmptiness } from "./container-state.js";
|
|
|
68
69
|
import { hasDisguisedText, hasForbiddenControls, validateNote } from "./decision.js";
|
|
69
70
|
import { parseReviewContext, reviewContextCustodyProblem } from "./review-context.js";
|
|
70
71
|
import { foldReview, manualReviewOnly } from "./proof.js";
|
|
71
|
-
import { approvalOf, digestOf, canonicalProfileJson, chainFromJson, profileDigestOf, profileFromJson, scopeAuthorityOf, routeParityProblem, parseAcceptanceCriteria, exactAcceptance, exactStringList, exactSafeIntegerOrNull
|
|
72
|
+
import { approvalOf, digestOf, canonicalProfileJson, chainFromJson, profileDigestOf, profileFromJson, scopeAuthorityOf, routeParityProblem, parseAcceptanceCriteria, exactAcceptance, exactStringList, exactSafeIntegerOrNull } from "./scope.js";
|
|
72
73
|
import { resolveScopeProfile, resolveRouteCandidates, exactPinOf, routeOfTask, agentChoicesFor } from "./agentconfig.js";
|
|
73
|
-
import { canonicalOverridesJson, canonicalRouteJson,
|
|
74
|
+
import { canonicalOverridesJson, canonicalRouteJson, isRiskLevel, isTaskSize, headroomFrom, makesNoPlan, legOf, overridesFromJson, phaseOfRole, recommendRoute, routeDigestOf, routeFromJson, routeProblems, routeStampProblem, ROUTE_ERA, PROVIDER_ID_LIST, } from "./phase-routing.js";
|
|
74
75
|
import { readAuthModeStrict } from "./keys.js";
|
|
75
76
|
import { modeDigestOf, modeTermsFromJson } from "./modes.js";
|
|
76
77
|
import { readVerifiedArtifact } from "./evidence.js";
|
|
77
|
-
import {
|
|
78
|
+
import {} from "./provider.js";
|
|
78
79
|
import { authenticate as runnerAuthenticate } from "./runner.js";
|
|
79
80
|
import { stampNewProjectLevel } from "./check-levels.js";
|
|
80
81
|
import { parseCheckProgressSnapshot } from "./check-progress.js";
|
|
@@ -718,7 +719,9 @@ const REGISTERED_AT_FILING = "again, as it was filed";
|
|
|
718
719
|
// (confinement_setting, one row; off unless an instance operator turns it on).
|
|
719
720
|
// v131 adds, and changes no saved row: where a project's builds may write (project_writes; agent-fence.ts, docs/plans/
|
|
720
721
|
// agent-baseline-hardening.md arc 2) — only their workspace unless an approver chose anywhere.
|
|
721
|
-
|
|
722
|
+
// v132 adds, and changes no saved row: how far a project's checks reach beyond the check fence (project_check_access;
|
|
723
|
+
// check-access.ts, docs/plans/check-access.md) — sandboxed unless an instance operator widened it.
|
|
724
|
+
export const SCHEMA_VERSION = 132;
|
|
722
725
|
/** v118: the tables merged into `credential`, in the order they move (a person's API tokens keep their ids first). */
|
|
723
726
|
export const V118_MERGED_TABLES = Object.freeze({ api_token: "person", coordinator_credential: "coordinator", lead_credential: "lead" });
|
|
724
727
|
/** The foreign keys that named a merged table, exactly as their DDL wrote them; each names `credential(id)` instead. */
|
|
@@ -851,7 +854,7 @@ const V115_DROPPED_REFERENCES = /\s+REFERENCES (?:contestant|contest|tournament_
|
|
|
851
854
|
* unchanged), and adds the nullable column in V125_ADDED_COLUMNS, checked the same way. v126 only adds a new table
|
|
852
855
|
* (task_source) and the columns in V126_ADDED_COLUMNS (null or 0), checked the same way.
|
|
853
856
|
*/
|
|
854
|
-
export const UPDATE_SAFE_MIGRATIONS = Object.freeze([108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131]);
|
|
857
|
+
export const UPDATE_SAFE_MIGRATIONS = Object.freeze([108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132]);
|
|
855
858
|
/** v119: the columns it adds, by table, with the value every row saved before it reads (toolroll-update's HISTORY_RULES). */
|
|
856
859
|
export const V119_ADDED_COLUMNS = Object.freeze({
|
|
857
860
|
task_ref: { planning_generation: { definition: "INTEGER NOT NULL DEFAULT 0", saved: "0" } },
|
|
@@ -988,6 +991,15 @@ CREATE TABLE IF NOT EXISTS project_writes (
|
|
|
988
991
|
updated_by TEXT NOT NULL
|
|
989
992
|
);
|
|
990
993
|
`;
|
|
994
|
+
/** v132: how far a project's checks reach beyond the check fence (check-access.ts CheckAccess). */
|
|
995
|
+
export const PROJECT_CHECK_ACCESS_SCHEMA = `
|
|
996
|
+
CREATE TABLE IF NOT EXISTS project_check_access (
|
|
997
|
+
repo TEXT PRIMARY KEY,
|
|
998
|
+
level TEXT NOT NULL CHECK (level IN ('sandboxed', 'sign-ins', 'unsandboxed')),
|
|
999
|
+
updated_at TEXT NOT NULL,
|
|
1000
|
+
updated_by TEXT NOT NULL
|
|
1001
|
+
);
|
|
1002
|
+
`;
|
|
991
1003
|
/** v129: a finished run's How it ran, derived from its own records on first view (display only: never a verdict). Its
|
|
992
1004
|
* parser version says when to derive again; `steps` says how complete the step detail was, and why not. */
|
|
993
1005
|
export const RUN_EFFICIENCY_SCHEMA = `
|
|
@@ -1177,8 +1189,6 @@ CREATE INDEX IF NOT EXISTS sso_identity_account ON sso_identity (account);
|
|
|
1177
1189
|
*/
|
|
1178
1190
|
/** The backend name the built-in store registers itself under. */
|
|
1179
1191
|
export const BUILT_IN = "built-in";
|
|
1180
|
-
/** States from which no further work is dispatched. */
|
|
1181
|
-
export const TERMINAL_STATES = ["done", "failed", "cancelled"];
|
|
1182
1192
|
export const LEAD_THREAD = { kind: "lead" };
|
|
1183
1193
|
/** A lead_message row as a LeadMessage. */
|
|
1184
1194
|
function readLeadMessageRow(row) {
|
|
@@ -1298,10 +1308,6 @@ function readFlowReceipt(row) {
|
|
|
1298
1308
|
const optional = (key) => row[key] === null || row[key] === undefined ? null : String(row[key]);
|
|
1299
1309
|
return { state, card, note: optional("note"), at: String(row["at"]), acceptedAt: optional("accepted_at"), itemJson: optional("item_json") };
|
|
1300
1310
|
}
|
|
1301
|
-
/** `kind:name`, the unambiguous way a requirement names a capability. */
|
|
1302
|
-
export function capabilityKey(capability) {
|
|
1303
|
-
return `${capability.kind}:${capability.name}`;
|
|
1304
|
-
}
|
|
1305
1311
|
/** Parse `kind:name`; a bare name is refused rather than guessed at. */
|
|
1306
1312
|
export function parseCapabilityKey(key) {
|
|
1307
1313
|
const split = key.indexOf(":");
|
|
@@ -3929,6 +3935,11 @@ function initializeStore(db, file) {
|
|
|
3929
3935
|
db.exec(CONFINEMENT_SCHEMA);
|
|
3930
3936
|
// v131: where a project's builds may write (a new table only).
|
|
3931
3937
|
db.exec(PROJECT_WRITES_SCHEMA);
|
|
3938
|
+
// v132: how far a project's checks reach beyond the check fence (a new table), and the level each build's checks ran
|
|
3939
|
+
// at (run.sandbox_access; saved rows read NULL: sandboxed).
|
|
3940
|
+
db.exec(PROJECT_CHECK_ACCESS_SCHEMA);
|
|
3941
|
+
addColumn(db, "run", "sandbox_access", "TEXT");
|
|
3942
|
+
addColumn(db, "run", "sandbox_network", "TEXT");
|
|
3932
3943
|
// v123: a flow that runs on its own (flow-unattended.ts): its grants and daily runs (new tables), and which authority
|
|
3933
3944
|
// armed a plan's unchanged-plan approval (plan_authorization.basis, V123_ADDED_COLUMNS: saved rows read the mode).
|
|
3934
3945
|
db.exec(FLOW_UNATTENDED_SCHEMA);
|
|
@@ -6413,11 +6424,6 @@ export class Store {
|
|
|
6413
6424
|
.get(mutation.idempotencyKey);
|
|
6414
6425
|
return row !== undefined;
|
|
6415
6426
|
}
|
|
6416
|
-
enqueueExternalIntent(mirror, kind, body, now) {
|
|
6417
|
-
this.db
|
|
6418
|
-
.prepare("INSERT INTO external_intent (mirror, kind, body, created_at) VALUES (?, ?, ?, ?)")
|
|
6419
|
-
.run(mirror, kind, body, now.toISOString());
|
|
6420
|
-
}
|
|
6421
6427
|
pendingExternalIntents(limit = 50) {
|
|
6422
6428
|
return this.db
|
|
6423
6429
|
.prepare("SELECT id, mirror, kind, body, attempts FROM external_intent WHERE state = 'pending' ORDER BY id LIMIT ?")
|
|
@@ -6740,6 +6746,19 @@ export class Store {
|
|
|
6740
6746
|
.get(taskId);
|
|
6741
6747
|
if (writtenByLead !== undefined)
|
|
6742
6748
|
return false;
|
|
6749
|
+
// CHECK ACCESS (v132, check-access.ts): a scope whose checks reach beyond the fence (sign-ins, or no sandbox at
|
|
6750
|
+
// all) seals only on a person's password ceremony — never a mode, a grant, a chat confirmation or a carry.
|
|
6751
|
+
const profileText = this.db.prepare("SELECT profile_json FROM task_scope WHERE task_id = ?").get(taskId)?.["profile_json"];
|
|
6752
|
+
let sealedAccess;
|
|
6753
|
+
try {
|
|
6754
|
+
const stored = typeof profileText === "string" ? JSON.parse(profileText) : null;
|
|
6755
|
+
sealedAccess = stored?.profile?.["checkAccess"];
|
|
6756
|
+
}
|
|
6757
|
+
catch {
|
|
6758
|
+
sealedAccess = "unreadable";
|
|
6759
|
+
}
|
|
6760
|
+
if (sealedAccess !== undefined)
|
|
6761
|
+
return false;
|
|
6743
6762
|
}
|
|
6744
6763
|
// SEPARATION OF DUTIES (v102), enforced in the primitive so no road
|
|
6745
6764
|
// around it exists: a project's rules can refuse the requester, and
|
|
@@ -11423,54 +11442,6 @@ export class Store {
|
|
|
11423
11442
|
return row === undefined ? null : { leaseId: String(row["lease_id"]) };
|
|
11424
11443
|
}
|
|
11425
11444
|
// ---- worker-process slots (v14, finding 26) ----------------------------
|
|
11426
|
-
reserveExecutionSlots(runner, count, now) {
|
|
11427
|
-
return this.transact(() => {
|
|
11428
|
-
const ids = [];
|
|
11429
|
-
for (let index = 0; index < count; index++) {
|
|
11430
|
-
const inserted = this.db
|
|
11431
|
-
.prepare("INSERT INTO execution_slot (runner, reserved_at) VALUES (?, ?)")
|
|
11432
|
-
.run(runner, now.toISOString());
|
|
11433
|
-
ids.push(Number(inserted.lastInsertRowid));
|
|
11434
|
-
}
|
|
11435
|
-
return ids;
|
|
11436
|
-
});
|
|
11437
|
-
}
|
|
11438
|
-
/** reserved → running happens in the stamp-before-spawn moment, carrying
|
|
11439
|
-
* the process group so recovery can ask the OS before freeing capacity. */
|
|
11440
|
-
markSlotRunning(id, facts, now) {
|
|
11441
|
-
const changed = this.db
|
|
11442
|
-
.prepare(`UPDATE execution_slot SET state = 'running', run = ?, incarnation = ?, process_group = ?, running_at = ?
|
|
11443
|
-
WHERE id = ? AND state = 'reserved'`)
|
|
11444
|
-
.run(facts.run, facts.incarnation ?? null, facts.processGroup ?? null, now.toISOString(), id);
|
|
11445
|
-
return Number(changed.changes) === 1;
|
|
11446
|
-
}
|
|
11447
|
-
/** A correction process replaces the exited provider in the same open
|
|
11448
|
-
* root attempt. It may refresh only that attempt's exact occupied slot. */
|
|
11449
|
-
refreshSlotProcess(id, facts) {
|
|
11450
|
-
if (!Number.isInteger(facts.processGroup) || facts.processGroup <= 0)
|
|
11451
|
-
return false;
|
|
11452
|
-
const changed = this.db.prepare(`UPDATE execution_slot SET process_group = ?
|
|
11453
|
-
WHERE id = ? AND state = 'running' AND run = ? AND incarnation IS ?
|
|
11454
|
-
AND EXISTS (SELECT 1 FROM run WHERE id = ? AND outcome IS NULL)`)
|
|
11455
|
-
.run(facts.processGroup, id, facts.run, facts.incarnation ?? null, facts.run);
|
|
11456
|
-
return Number(changed.changes) === 1;
|
|
11457
|
-
}
|
|
11458
|
-
releaseExecutionSlot(id, now) {
|
|
11459
|
-
const changed = this.db
|
|
11460
|
-
.prepare("UPDATE execution_slot SET state = 'released', released_at = ? WHERE id = ? AND state IN ('reserved','running')")
|
|
11461
|
-
.run(now.toISOString(), id);
|
|
11462
|
-
return Number(changed.changes) === 1;
|
|
11463
|
-
}
|
|
11464
|
-
liveSlotCount(runner) {
|
|
11465
|
-
const row = this.db
|
|
11466
|
-
.prepare("SELECT COUNT(*) AS n FROM execution_slot WHERE runner = ? AND state IN ('reserved','running')")
|
|
11467
|
-
.get(runner);
|
|
11468
|
-
return Number(row?.["n"] ?? 0);
|
|
11469
|
-
}
|
|
11470
|
-
getExecutionSlot(id) {
|
|
11471
|
-
const row = this.db.prepare("SELECT * FROM execution_slot WHERE id = ?").get(id);
|
|
11472
|
-
return row === undefined ? null : readExecutionSlot(row);
|
|
11473
|
-
}
|
|
11474
11445
|
// ---- durable ceremony nonces (v14, findings 21/30) ---------------------
|
|
11475
11446
|
/** Minted from a POST, never a GET. Bounded per approver; expired rows
|
|
11476
11447
|
* are swept rather than accumulating. */
|
|
@@ -13528,40 +13499,6 @@ export class Store {
|
|
|
13528
13499
|
repo: row["task_repo"] === null ? null : String(row["task_repo"]),
|
|
13529
13500
|
}));
|
|
13530
13501
|
}
|
|
13531
|
-
/**
|
|
13532
|
-
* The Work destination's one bounded page (workspace package 1 query):
|
|
13533
|
-
* the newest tasks the viewer may see, with the viewer's already-proved
|
|
13534
|
-
* admission bound BEFORE the ordering and the limit — never a wider
|
|
13535
|
-
* read post-filtered in the caller. `admitted` is the resolved
|
|
13536
|
-
* permitted repo set: `null` means unrestricted (every placed row),
|
|
13537
|
-
* `[]` means no placed project is permitted (no placed row at all).
|
|
13538
|
-
* `includeUnplaced` says whether NULL-repo rows belong to this viewer;
|
|
13539
|
-
* an account restricted to projects never sees them, so they must not
|
|
13540
|
-
* spend its page. Every predicate is a bound parameter; the limit is
|
|
13541
|
-
* clamped here, not trusted from the caller. Read-only; the legacy
|
|
13542
|
-
* `listTasksScoped` is untouched.
|
|
13543
|
-
*/
|
|
13544
|
-
listWorkTasksAdmitted(admitted, includeUnplaced, limit) {
|
|
13545
|
-
const page = Math.max(1, Math.min(Math.floor(limit), 500));
|
|
13546
|
-
// SQLite accepts an empty IN list, so an empty admission binds as
|
|
13547
|
-
// "no placed row" without any special SQL shape.
|
|
13548
|
-
const rows = this.db
|
|
13549
|
-
.prepare(`SELECT task.*, task_ref.repo AS task_repo FROM task
|
|
13550
|
-
JOIN task_ref ON task_ref.backend = ? AND task_ref.external_id = task.id
|
|
13551
|
-
WHERE ((task_ref.repo IS NULL AND ? = 1)
|
|
13552
|
-
OR (task_ref.repo IS NOT NULL AND (? = 1 OR task_ref.repo IN (${(admitted ?? []).map(() => "?").join(",")}))))
|
|
13553
|
-
ORDER BY task.created_at DESC, task.id DESC LIMIT ?`)
|
|
13554
|
-
.all(BUILT_IN, includeUnplaced ? 1 : 0, admitted === null ? 1 : 0, ...(admitted ?? []), page);
|
|
13555
|
-
return rows.map(row => ({
|
|
13556
|
-
id: String(row["id"]),
|
|
13557
|
-
title: String(row["title"]),
|
|
13558
|
-
state: String(row["state"]),
|
|
13559
|
-
createdAt: String(row["created_at"]),
|
|
13560
|
-
updatedAt: String(row["updated_at"]),
|
|
13561
|
-
priority: row["priority"] === null || row["priority"] === undefined ? 0 : Number(row["priority"]),
|
|
13562
|
-
repo: row["task_repo"] === null ? null : String(row["task_repo"]),
|
|
13563
|
-
}));
|
|
13564
|
-
}
|
|
13565
13502
|
/** Newest first, because the question is almost always "what just happened". */
|
|
13566
13503
|
runsFor(taskRef) {
|
|
13567
13504
|
return this.db
|
|
@@ -14507,10 +14444,6 @@ export class Store {
|
|
|
14507
14444
|
.run(incident.run, incident.kind, now.toISOString());
|
|
14508
14445
|
return Number(inserted.lastInsertRowid);
|
|
14509
14446
|
}
|
|
14510
|
-
incidentForRun(run) {
|
|
14511
|
-
const row = this.db.prepare("SELECT * FROM incident WHERE run = ?").get(run);
|
|
14512
|
-
return row === undefined ? null : readIncident(row);
|
|
14513
|
-
}
|
|
14514
14447
|
/** Unresolved, task attached, oldest first. No time window: these do not age out. */
|
|
14515
14448
|
openIncidents(repo = null) {
|
|
14516
14449
|
return this.db
|
|
@@ -14872,14 +14805,6 @@ export class Store {
|
|
|
14872
14805
|
return Number(changes) === 1 ? { generation } : null;
|
|
14873
14806
|
});
|
|
14874
14807
|
}
|
|
14875
|
-
/** Renew the lease AND re-prove the generation in one CAS — the last
|
|
14876
|
-
* gate before the external call; a failed re-proof aborts the attempt. */
|
|
14877
|
-
renewMergeClaim(id, generation, leaseMs, now) {
|
|
14878
|
-
const { changes } = this.db
|
|
14879
|
-
.prepare("UPDATE merge_intent SET claimed_until = ? WHERE id = ? AND state = 'claimed' AND generation = ?")
|
|
14880
|
-
.run(new Date(now.getTime() + leaseMs).toISOString(), id, generation);
|
|
14881
|
-
return Number(changes) === 1;
|
|
14882
|
-
}
|
|
14883
14808
|
/** STATE-SPECIFIC and TERMINALLY MONOTONIC (E1): the writer names the
|
|
14884
14809
|
* state it believes it owns, and a terminal row is never overwritten —
|
|
14885
14810
|
* a stale claim owner cannot un-merge what the observer already settled,
|
|
@@ -15029,14 +14954,6 @@ export class Store {
|
|
|
15029
14954
|
firingDeadline: row["firing_deadline"] === null ? null : String(row["firing_deadline"]),
|
|
15030
14955
|
};
|
|
15031
14956
|
}
|
|
15032
|
-
latestObservation(githubRepo, prNumber) {
|
|
15033
|
-
const row = this.db
|
|
15034
|
-
.prepare("SELECT head_sha, state, observed_at, generation FROM ci_observation WHERE github_repo = ? AND pr_number = ?")
|
|
15035
|
-
.get(githubRepo, prNumber);
|
|
15036
|
-
return row === undefined || String(row["observed_at"]) === ""
|
|
15037
|
-
? null
|
|
15038
|
-
: { headSha: String(row["head_sha"]), state: String(row["state"]), observedAt: String(row["observed_at"]), generation: Number(row["generation"]) };
|
|
15039
|
-
}
|
|
15040
14957
|
/** Revocation is immediate: intents not yet pushed die with the grant. */
|
|
15041
14958
|
revokePublicationGrant(repo, by, now) {
|
|
15042
14959
|
const { changes } = this.db
|
|
@@ -16635,9 +16552,6 @@ export class Store {
|
|
|
16635
16552
|
const row = this.db.prepare("SELECT * FROM mate_turn WHERE approver = ? AND state IN ('queued','running') ORDER BY id DESC LIMIT 1").get(approver);
|
|
16636
16553
|
return row === undefined ? null : readLeadTurn(row);
|
|
16637
16554
|
}
|
|
16638
|
-
leadStepsOf(leadTurn) {
|
|
16639
|
-
return this.db.prepare("SELECT * FROM chat_turn WHERE mate_turn = ? ORDER BY id ASC").all(leadTurn).map(readChatTurn);
|
|
16640
|
-
}
|
|
16641
16555
|
// ---- projects ------------------------------------------------------------
|
|
16642
16556
|
/** Remember a project was opened. Upsert keeps added_at; recency always moves.
|
|
16643
16557
|
* A project added for the first time starts on Quick checks. */
|
|
@@ -16808,50 +16722,6 @@ export class Store {
|
|
|
16808
16722
|
blockerRepo: row["blocker_repo"] === null || row["blocker_repo"] === undefined ? null : String(row["blocker_repo"]),
|
|
16809
16723
|
}));
|
|
16810
16724
|
}
|
|
16811
|
-
/**
|
|
16812
|
-
* Active work only, running first — the Work tab's page. Terminal history
|
|
16813
|
-
* belongs to Done and the build ledger, never to this list.
|
|
16814
|
-
*/
|
|
16815
|
-
listActiveWorkScoped(repo, now, limit = 50) {
|
|
16816
|
-
const page = Math.max(1, Math.min(Math.floor(limit), 100));
|
|
16817
|
-
return this.db
|
|
16818
|
-
.prepare(`SELECT task.*, task_ref.repo AS task_repo, task_ref.strikes AS ref_strikes,
|
|
16819
|
-
EXISTS (
|
|
16820
|
-
SELECT 1 FROM task_scope
|
|
16821
|
-
WHERE task_scope.task_id = task.id AND task_scope.approved_digest = task_scope.digest
|
|
16822
|
-
AND task_scope.approved_at IS NOT NULL
|
|
16823
|
-
) AS approved,
|
|
16824
|
-
EXISTS (
|
|
16825
|
-
SELECT 1 FROM hold
|
|
16826
|
-
WHERE hold.task_ref = task_ref.id AND (hold.until IS NULL OR hold.until > ?)
|
|
16827
|
-
) AS held,
|
|
16828
|
-
(SELECT claim.runner FROM claim
|
|
16829
|
-
WHERE claim.task_ref = task_ref.id AND claim.released_at IS NULL AND claim.expires_at > ?
|
|
16830
|
-
AND claim.lease_generation = (
|
|
16831
|
-
SELECT MAX(newest.lease_generation) FROM claim AS newest
|
|
16832
|
-
WHERE newest.task_ref = claim.task_ref)
|
|
16833
|
-
LIMIT 1) AS building_by
|
|
16834
|
-
FROM task
|
|
16835
|
-
JOIN task_ref ON task_ref.backend = ? AND task_ref.external_id = task.id
|
|
16836
|
-
WHERE task.state IN ('running','queued')
|
|
16837
|
-
AND (? IS NULL OR task_ref.repo IS NULL OR task_ref.repo = ?)
|
|
16838
|
-
ORDER BY CASE task.state WHEN 'running' THEN 0 ELSE 1 END, task.updated_at DESC, task.id DESC
|
|
16839
|
-
LIMIT ?`)
|
|
16840
|
-
.all(now.toISOString(), now.toISOString(), BUILT_IN, repo, repo, page)
|
|
16841
|
-
.map(row => ({
|
|
16842
|
-
id: String(row["id"]),
|
|
16843
|
-
title: String(row["title"]),
|
|
16844
|
-
state: String(row["state"]),
|
|
16845
|
-
createdAt: String(row["created_at"]),
|
|
16846
|
-
updatedAt: String(row["updated_at"]),
|
|
16847
|
-
priority: row["priority"] === null || row["priority"] === undefined ? 0 : Number(row["priority"]),
|
|
16848
|
-
repo: row["task_repo"] === null ? null : String(row["task_repo"]),
|
|
16849
|
-
approved: Number(row["approved"]) === 1,
|
|
16850
|
-
held: Number(row["held"]) === 1,
|
|
16851
|
-
buildingBy: row["building_by"] === null ? null : String(row["building_by"]),
|
|
16852
|
-
strikes: Number(row["ref_strikes"]),
|
|
16853
|
-
}));
|
|
16854
|
-
}
|
|
16855
16725
|
/**
|
|
16856
16726
|
* Completed work, one row per done task with its final accepted run and
|
|
16857
16727
|
* any publication attached — never composed from separate task and PR
|
|
@@ -19147,6 +19017,59 @@ export class Store {
|
|
|
19147
19017
|
this.db.prepare("UPDATE lead_queue SET context_json = NULL WHERE message = ?").run(message);
|
|
19148
19018
|
this.db.prepare("DELETE FROM memory_search WHERE kind = 'conversation' AND ref = ?").run(`message:${message}`);
|
|
19149
19019
|
}
|
|
19020
|
+
// ---- check access (v132, check-access.ts). Kept last in the class: the freshness tripwire pins method positions.
|
|
19021
|
+
/** How far a project's checks reach beyond the check fence: its saved level, or sandboxed. */
|
|
19022
|
+
projectCheckAccess(repo) {
|
|
19023
|
+
// A damaged row reads as the narrowest level, never a wider one.
|
|
19024
|
+
return checkAccessOf(this.db.prepare("SELECT level FROM project_check_access WHERE repo = ?").get(repo)?.["level"]);
|
|
19025
|
+
}
|
|
19026
|
+
/** Who last changed a project's check access, and when (null: never changed). */
|
|
19027
|
+
projectCheckAccessChange(repo) {
|
|
19028
|
+
const row = this.db.prepare("SELECT updated_by, updated_at FROM project_check_access WHERE repo = ?").get(repo);
|
|
19029
|
+
return row === undefined ? null : { by: String(row["updated_by"]), at: String(row["updated_at"]) };
|
|
19030
|
+
}
|
|
19031
|
+
/** The level a run's setup and checks ran at (its sealed approval's, or unsandboxed where no OS fence exists). Its
|
|
19032
|
+
* follow-up and batch checks never run wider than this. */
|
|
19033
|
+
stampRunCheckAccess(runId, level, network = null) {
|
|
19034
|
+
this.db.prepare("UPDATE run SET sandbox_access = ?, sandbox_network = ? WHERE id = ?").run(level, network === null ? null : JSON.stringify(network), runId);
|
|
19035
|
+
}
|
|
19036
|
+
/** The network a run's sign-ins checks ran under (null: none sealed; such a run's later checks run sandboxed). */
|
|
19037
|
+
runCheckNetwork(runId) {
|
|
19038
|
+
const text = this.db.prepare("SELECT sandbox_network FROM run WHERE id = ?").get(runId)?.["sandbox_network"];
|
|
19039
|
+
if (typeof text !== "string")
|
|
19040
|
+
return null;
|
|
19041
|
+
try {
|
|
19042
|
+
return networkFromValue(JSON.parse(text));
|
|
19043
|
+
}
|
|
19044
|
+
catch {
|
|
19045
|
+
return null;
|
|
19046
|
+
}
|
|
19047
|
+
}
|
|
19048
|
+
/** The level a run's checks ran at: missing or unreadable reads as sandboxed. */
|
|
19049
|
+
runCheckAccess(runId) {
|
|
19050
|
+
return checkAccessOf(this.db.prepare("SELECT sandbox_access FROM run WHERE id = ?").get(runId)?.["sandbox_access"]);
|
|
19051
|
+
}
|
|
19052
|
+
/** A project's check access changed: saved, and in the ledger as before → after. `may` is what the caller proved:
|
|
19053
|
+
* "widen" only after an instance operator's password typed now; anything else may only narrow. The level is read again
|
|
19054
|
+
* and compared in the same transaction as the write, so a change made while the caller was proving itself can't turn
|
|
19055
|
+
* a narrowing into a widening. Refused: null, nothing written. */
|
|
19056
|
+
setProjectCheckAccess(repo, level, by, now, authority) {
|
|
19057
|
+
return this.transact(() => {
|
|
19058
|
+
const before = this.projectCheckAccess(repo);
|
|
19059
|
+
// Who is acting is proved again here too: an account revoked, demoted or moved off the project while its password
|
|
19060
|
+
// was being checked changes nothing.
|
|
19061
|
+
const account = this.accountOf(by);
|
|
19062
|
+
if (account === null || account.revokedAt !== null || account.role !== "approver" || !this.accountCanAccess(by, repo))
|
|
19063
|
+
return null;
|
|
19064
|
+
if (widerThan(level, before) && (authority.may !== "widen" || !this.isInstanceOperator(by)))
|
|
19065
|
+
return null;
|
|
19066
|
+
this.db.prepare(`INSERT INTO project_check_access (repo, level, updated_at, updated_by) VALUES (?, ?, ?, ?)
|
|
19067
|
+
ON CONFLICT (repo) DO UPDATE SET level = excluded.level, updated_at = excluded.updated_at, updated_by = excluded.updated_by`).run(repo, level, now.toISOString(), by);
|
|
19068
|
+
const after = this.projectCheckAccess(repo);
|
|
19069
|
+
this.recordPolicy(by, repo, "check access", before, after, now);
|
|
19070
|
+
return { before, after };
|
|
19071
|
+
});
|
|
19072
|
+
}
|
|
19150
19073
|
}
|
|
19151
19074
|
/** A stored count or id that must be a whole, non-negative number — a
|
|
19152
19075
|
* NULL, an empty string, a fraction, or text reads as null (v48 authority repair). */
|
|
@@ -19544,20 +19467,6 @@ function readTaskRef(row) {
|
|
|
19544
19467
|
origin: String(row["origin"]) === "ours" ? "ours" : "theirs",
|
|
19545
19468
|
};
|
|
19546
19469
|
}
|
|
19547
|
-
function readExecutionSlot(row) {
|
|
19548
|
-
const maybe = (key) => (row[key] === null || row[key] === undefined ? null : String(row[key]));
|
|
19549
|
-
return {
|
|
19550
|
-
id: Number(row["id"]),
|
|
19551
|
-
runner: String(row["runner"]),
|
|
19552
|
-
state: String(row["state"]),
|
|
19553
|
-
run: row["run"] === null ? null : Number(row["run"]),
|
|
19554
|
-
incarnation: maybe("incarnation"),
|
|
19555
|
-
processGroup: row["process_group"] === null ? null : Number(row["process_group"]),
|
|
19556
|
-
reservedAt: String(row["reserved_at"]),
|
|
19557
|
-
runningAt: maybe("running_at"),
|
|
19558
|
-
releasedAt: maybe("released_at"),
|
|
19559
|
-
};
|
|
19560
|
-
}
|
|
19561
19470
|
function readChatTurn(row) {
|
|
19562
19471
|
const maybe = (key) => (row[key] === null || row[key] === undefined ? null : String(row[key]));
|
|
19563
19472
|
const maybeN = (key) => (row[key] === null || row[key] === undefined ? null : Number(row[key]));
|
|
@@ -20,7 +20,6 @@ export declare const REVIEW_OUTPUT_LIMITS: {
|
|
|
20
20
|
readonly path: 300;
|
|
21
21
|
};
|
|
22
22
|
export declare const REVIEW_NOTE_CODE_POINTS: number;
|
|
23
|
-
export declare const REVIEW_NOTE_GUIDANCE: string;
|
|
24
23
|
export type StructuredNormalization = {
|
|
25
24
|
text: string;
|
|
26
25
|
changed: boolean;
|
|
@@ -16,7 +16,6 @@ export const STRUCTURED_REPAIR_TIMEOUT_MS = 5 * 60_000;
|
|
|
16
16
|
* notes concise without imposing a second, stricter transport contract. */
|
|
17
17
|
export const REVIEW_OUTPUT_LIMITS = { comments: 40, criteria: 12, note: TEXT_LIMITS.reviewNote, path: TEXT_LIMITS.reviewPath };
|
|
18
18
|
export const REVIEW_NOTE_CODE_POINTS = Math.floor(REVIEW_OUTPUT_LIMITS.note / 2);
|
|
19
|
-
export const REVIEW_NOTE_GUIDANCE = `Keep each comment and criterion note to one concise finding and its evidence. Aim for ${REVIEW_NOTE_CODE_POINTS} characters or fewer; the hard limit is ${REVIEW_OUTPUT_LIMITS.note} UTF-16 units. Preserve the conclusion and any needed provenance; omit repeated rubric text.`;
|
|
20
19
|
/** Strip only unambiguous, whole-payload wrappers. */
|
|
21
20
|
export function normalizeStructuredJson(raw) {
|
|
22
21
|
let text = raw;
|
package/dist/subagent-work.d.ts
CHANGED
package/dist/subagent-work.js
CHANGED
|
@@ -304,5 +304,3 @@ export function answerSubagentQuestion(store, id, answer, now) {
|
|
|
304
304
|
return { ok: true, said: question.toolCall === null ? "Answered. It picks the card up again now." : choice?.id === APPROVE ? "Approved. The call is made now, exactly as shown." : "It won't make that call. It picks the card up again now." };
|
|
305
305
|
});
|
|
306
306
|
}
|
|
307
|
-
/** A card's link, for messages about it. */
|
|
308
|
-
export const subagentCardHref = (flow, card) => flowCardHref(flow, card);
|
package/dist/subagents.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type CommandRunner } from "./claude-print.js";
|
|
2
2
|
import type { ContractResult } from "./contracts/contract.js";
|
|
3
3
|
import { type TurnAnswer } from "./contracts/subagent-turn.js";
|
|
4
4
|
import { type Overrun } from "./text-limits.js";
|
|
@@ -13,7 +13,6 @@ export type Soul = {
|
|
|
13
13
|
};
|
|
14
14
|
/** A handle from a name: "Maya" → "maya". */
|
|
15
15
|
export declare const handleOf: (name: string) => string;
|
|
16
|
-
export declare const isHandle: (value: unknown) => value is string;
|
|
17
16
|
/** A soul file read: its name and role (front matter) and its sections. */
|
|
18
17
|
export declare function parseSoul(text: string): {
|
|
19
18
|
ok: true;
|
|
@@ -139,7 +138,6 @@ export type TurnReply = ({
|
|
|
139
138
|
/** Runs one turn; injectable so tests never spend a subscription turn. */
|
|
140
139
|
export type TurnRunner = (request: TurnRequest) => Promise<TurnReply>;
|
|
141
140
|
export declare const TURN_TIMEOUT_MS = 180000;
|
|
142
|
-
type CommandRunner = (file: string, args: readonly string[], options: Parameters<typeof run>[2]) => Promise<ExecResult>;
|
|
143
141
|
/** The production runner: Claude through this computer's sign-in, answering in the turn schema (TURN_MODEL_SCHEMA), with no tools, no MCP servers and no repository. */
|
|
144
142
|
export declare function claudeTurnRunner(runner?: CommandRunner): TurnRunner;
|
|
145
143
|
/** How a subagent is named to people: "Maya · Support". */
|
package/dist/subagents.js
CHANGED
|
@@ -17,21 +17,17 @@
|
|
|
17
17
|
* spending — those stay with people (or a signed hands-off mode).
|
|
18
18
|
*/
|
|
19
19
|
import { Buffer } from "node:buffer";
|
|
20
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
21
|
-
import { tmpdir } from "node:os";
|
|
22
|
-
import { join } from "node:path";
|
|
23
20
|
import { strictJsonParse } from "./converse.js";
|
|
24
21
|
import { scanForSecrets } from "./evidence.js";
|
|
25
22
|
import { run } from "./exec.js";
|
|
23
|
+
import { CLAUDE_NO_TOOLS_ARGS, CLAUDE_SIGNED_OUT, claudeJsonBody, claudeModelArgs, inScratchDir } from "./claude-print.js";
|
|
26
24
|
import { ALL_CREDENTIAL_ENV } from "./provider.js";
|
|
27
25
|
import { readTurnAnswer, TURN_MODEL_SCHEMA } from "./contracts/subagent-turn.js";
|
|
28
26
|
import { LIMITS } from "./decision.js";
|
|
29
27
|
import { overruns, TEXT_LIMITS } from "./text-limits.js";
|
|
30
28
|
export const SOUL_CHARS = 12_000;
|
|
31
|
-
const HANDLE = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
32
29
|
/** A handle from a name: "Maya" → "maya". */
|
|
33
30
|
export const handleOf = (name) => name.toLowerCase().normalize("NFKD").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32) || "subagent";
|
|
34
|
-
export const isHandle = (value) => typeof value === "string" && HANDLE.test(value);
|
|
35
31
|
/** A soul file read: its name and role (front matter) and its sections. */
|
|
36
32
|
export function parseSoul(text) {
|
|
37
33
|
const normal = text.replace(/\r\n?/g, "\n").trim();
|
|
@@ -220,36 +216,27 @@ export function readTurn(value, context) {
|
|
|
220
216
|
export const TURN_TIMEOUT_MS = 180_000;
|
|
221
217
|
/** The production runner: Claude through this computer's sign-in, answering in the turn schema (TURN_MODEL_SCHEMA), with no tools, no MCP servers and no repository. */
|
|
222
218
|
export function claudeTurnRunner(runner = run) {
|
|
223
|
-
return async (
|
|
224
|
-
const dir = mkdtempSync(join(tmpdir(), "standing-orders-subagent-"));
|
|
219
|
+
return request => inScratchDir("standing-orders-subagent-", async (dir) => {
|
|
225
220
|
const started = Date.now();
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const body = parsed.ok && typeof parsed.value === "object" && parsed.value !== null && !Array.isArray(parsed.value) ? parsed.value : null;
|
|
238
|
-
if (result.code !== 0 || body === null || body["is_error"] === true || body["subtype"] !== "success") {
|
|
239
|
-
const why = typeof body?.["subtype"] === "string" && body["subtype"] !== "success" ? ` (${String(body["subtype"]).replace(/_/g, " ").slice(0, 60)})` : result.code !== 0 ? ` (exit ${result.code})` : "";
|
|
240
|
-
return { ok: false, said: /not logged in|login|authenticat/i.test(`${result.stdout}${result.stderr}`) ? "Claude isn't signed in on this computer." : `Claude couldn't decide${why}.` };
|
|
241
|
-
}
|
|
242
|
-
const cost = costOf(body);
|
|
243
|
-
if (body["structured_output"] !== undefined && body["structured_output"] !== null)
|
|
244
|
-
return { ok: true, value: body["structured_output"], ms: Date.now() - started, ...cost };
|
|
245
|
-
const text = typeof body["result"] === "string" ? body["result"].trim().replace(/^```(?:json)?\n?|\n?```$/g, "") : "";
|
|
246
|
-
const inner = strictJsonParse(Buffer.from(text, "utf8"), 64 * 1024, 8);
|
|
247
|
-
return inner.ok ? { ok: true, value: inner.value, ms: Date.now() - started, ...cost } : { ok: false, said: "Claude's answer wasn't a decision.", ...cost };
|
|
221
|
+
const result = await runner("claude", [
|
|
222
|
+
"-p", "--output-format", "json", "--json-schema", JSON.stringify(TURN_MODEL_SCHEMA), ...CLAUDE_NO_TOOLS_ARGS, ...claudeModelArgs(request.model),
|
|
223
|
+
], { cwd: dir, stdin: request.prompt, timeoutMs: request.timeoutMs, maxBuffer: 512 * 1024, omitEnv: ALL_CREDENTIAL_ENV, processGroup: true });
|
|
224
|
+
if (result.notFound)
|
|
225
|
+
return { ok: false, said: "Claude isn't installed on this computer." };
|
|
226
|
+
if (result.timedOut)
|
|
227
|
+
return { ok: false, said: "Claude took too long to decide." };
|
|
228
|
+
const body = claudeJsonBody(result.stdout, 512 * 1024);
|
|
229
|
+
if (result.code !== 0 || body === null || body["is_error"] === true || body["subtype"] !== "success") {
|
|
230
|
+
const why = typeof body?.["subtype"] === "string" && body["subtype"] !== "success" ? ` (${String(body["subtype"]).replace(/_/g, " ").slice(0, 60)})` : result.code !== 0 ? ` (exit ${result.code})` : "";
|
|
231
|
+
return { ok: false, said: CLAUDE_SIGNED_OUT.test(`${result.stdout}${result.stderr}`) ? "Claude isn't signed in on this computer." : `Claude couldn't decide${why}.` };
|
|
248
232
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
233
|
+
const cost = costOf(body);
|
|
234
|
+
if (body["structured_output"] !== undefined && body["structured_output"] !== null)
|
|
235
|
+
return { ok: true, value: body["structured_output"], ms: Date.now() - started, ...cost };
|
|
236
|
+
const text = typeof body["result"] === "string" ? body["result"].trim().replace(/^```(?:json)?\n?|\n?```$/g, "") : "";
|
|
237
|
+
const inner = strictJsonParse(Buffer.from(text, "utf8"), 64 * 1024, 8);
|
|
238
|
+
return inner.ok ? { ok: true, value: inner.value, ms: Date.now() - started, ...cost } : { ok: false, said: "Claude's answer wasn't a decision.", ...cost };
|
|
239
|
+
});
|
|
253
240
|
}
|
|
254
241
|
/** What the CLI says a turn cost, and its tokens (cache reads and writes count as input). */
|
|
255
242
|
function costOf(body) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type LeadHistoryMessage, type LeadProviderAnswer, type LeadToolSchema } from "./converse.js";
|
|
2
|
-
import {
|
|
2
|
+
import { type CommandRunner } from "./claude-print.js";
|
|
3
3
|
import type { SubscriptionChatProviderId } from "./store.js";
|
|
4
4
|
export type SubscriptionLeadRequest = {
|
|
5
5
|
provider: SubscriptionChatProviderId;
|
|
@@ -27,7 +27,6 @@ export type SubscriptionLeadRunner = (request: SubscriptionLeadRequest) => Promi
|
|
|
27
27
|
ok: false;
|
|
28
28
|
problem: string;
|
|
29
29
|
}>;
|
|
30
|
-
type CommandRunner = (file: string, args: readonly string[], options: Parameters<typeof run>[2]) => Promise<ExecResult>;
|
|
31
30
|
/** The model-visible request. Tool results are already redacted by leadView. */
|
|
32
31
|
export declare function composeSubscriptionLeadPrompt(request: Omit<SubscriptionLeadRequest, "provider" | "model" | "timeoutMs" | "signal">): string;
|
|
33
32
|
/** Strictly turn the harness's structured output into the provider-neutral answer. */
|
|
@@ -49,4 +48,3 @@ export declare function performSubscriptionLeadRequest(request: SubscriptionLead
|
|
|
49
48
|
ok: false;
|
|
50
49
|
problem: string;
|
|
51
50
|
}>;
|
|
52
|
-
export {};
|
|
@@ -16,6 +16,7 @@ import { tmpdir } from "node:os";
|
|
|
16
16
|
import { join } from "node:path";
|
|
17
17
|
import { LEAD_MAX_CALLS_PER_STEP, LEAD_STEP_TEXT_CAP_BYTES, LEAD_TOOL_CALL_CAP_BYTES, LEAD_TOOL_CALL_ID_CAP_BYTES, strictJsonParse, tokenCount, } from "./converse.js";
|
|
18
18
|
import { run } from "./exec.js";
|
|
19
|
+
import { CLAUDE_NO_TOOLS_ARGS, claudeModelArgs } from "./claude-print.js";
|
|
19
20
|
import { claudeStreamReader } from "./lead-progress.js";
|
|
20
21
|
import { ALL_CREDENTIAL_ENV } from "./provider.js";
|
|
21
22
|
import { claudeLimitsOf, noteLimits } from "./provider-limits.js";
|
|
@@ -215,9 +216,7 @@ export async function performSubscriptionLeadRequest(request, runner = run) {
|
|
|
215
216
|
// Streaming (someone is watching): the same answer, delivered as
|
|
216
217
|
// events whose final `result` line is the whole structured reply.
|
|
217
218
|
"-p", ...(streaming ? ["--output-format", "stream-json", "--verbose", "--include-partial-messages"] : ["--output-format", "json"]), "--json-schema", JSON.stringify(schema),
|
|
218
|
-
|
|
219
|
-
"--permission-prompts", "none", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}',
|
|
220
|
-
...(request.model === "default" ? [] : ["--model", request.model]),
|
|
219
|
+
...CLAUDE_NO_TOOLS_ARGS, ...claudeModelArgs(request.model),
|
|
221
220
|
];
|
|
222
221
|
}
|
|
223
222
|
const result = await runner(command, args, {
|
package/dist/surface.js
CHANGED
|
@@ -353,6 +353,7 @@ const GUIDE_ROWS = [
|
|
|
353
353
|
operator("runner bind", "replace which repositories a worker may build in"),
|
|
354
354
|
operator("runner capacity", "set how many tasks a worker runs at once; running work carries on, the new number applies at the next claim"),
|
|
355
355
|
operator("project checks", "show or set a project's batch checks (--batch on|off, off by default): results that finish within 10 minutes share one full check on a temporary batch commit; nothing is merged into a real branch"),
|
|
356
|
+
operator("project check-access", "show or set how far a project's checks reach: sandboxed (the default), with model and GitHub sign-ins, or not sandboxed; an instance operator widens it with a password typed now"),
|
|
356
357
|
operator("project concurrency", "set how many of a project's tasks build at once (default 2, never past the worker's capacity); running work carries on"),
|
|
357
358
|
operator("coordinator mint", "mint the MCP filing credential — repo-bound, rate-limited, token shown once"),
|
|
358
359
|
operator("coordinator revoke", "revoke an MCP filing credential"),
|
|
@@ -450,7 +451,7 @@ const REMOTE_STEP_UP = [
|
|
|
450
451
|
// A person's own API tokens: a password step-up on the server's machine, or the console. Never with a token.
|
|
451
452
|
"tokens create", "tokens list", "tokens revoke", "tokens rotate",
|
|
452
453
|
"mode show", "mode set", "mode revoke", "chat-approval on", "review on", "review off", "config set", "config clear",
|
|
453
|
-
"verify set", "verify clear", "verify level", "project checks", "project concurrency", "intake grant", "intake clear", "webhook primary",
|
|
454
|
+
"verify set", "verify clear", "verify level", "project checks", "project check-access", "project concurrency", "intake grant", "intake clear", "webhook primary",
|
|
454
455
|
];
|
|
455
456
|
const REMOTE_YES = [
|
|
456
457
|
"status", "integrations", "ready", "gaps", "grants", "sync", "audit",
|
package/dist/task-sizing.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type SizeAnswer } from "./contracts/task-sizing.js";
|
|
2
|
-
import {
|
|
2
|
+
import { type CommandRunner } from "./claude-print.js";
|
|
3
3
|
import { type TaskSizing } from "./phase-routing.js";
|
|
4
4
|
import type { Store } from "./store.js";
|
|
5
5
|
/** The classifier's whole budget, from asking to answer. */
|
|
@@ -24,7 +24,6 @@ export declare function planningSignals(input: SizingInput): boolean;
|
|
|
24
24
|
export declare function heuristicSizing(input: SizingInput, why?: string | null): TaskSizing;
|
|
25
25
|
/** What the classifier reads: the task as data, bounded, keys blanked. */
|
|
26
26
|
export declare function sizingText(input: SizingInput): string;
|
|
27
|
-
type CommandRunner = (file: string, args: readonly string[], options: Parameters<typeof run>[2]) => Promise<ExecResult>;
|
|
28
27
|
/** A small Claude model on this computer's sign-in: no tools, no MCP servers, no repository, credential keys stripped.
|
|
29
28
|
* The abort ends it: a late answer never keeps a command (or its process) waiting. */
|
|
30
29
|
export declare function claudeSizer(runner?: CommandRunner, model?: string, budgetMs?: number): Sizer;
|
|
@@ -41,8 +40,6 @@ export declare function classifyTask(input: SizingInput, sizer: Sizer | null, bu
|
|
|
41
40
|
/** Install the process's classifier for filings (the CLI, the console, the worker); returns how to remove it. Unset,
|
|
42
41
|
* filings keep the description's size — tests never spend a turn. */
|
|
43
42
|
export declare function installFilingSizer(sizer: Sizer, budgetMs?: number): () => void;
|
|
44
|
-
/** Whether filings in this process are classified. */
|
|
45
|
-
export declare function filingSizerInstalled(): boolean;
|
|
46
43
|
/**
|
|
47
44
|
* After a filing: ask the classifier (within the budget) and apply its size
|
|
48
45
|
* to the still-unapproved task. Runs in the background; the filing already
|