toolroll 0.9.65 → 0.9.68
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 +93 -0
- package/README.md +20 -2
- 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 +69 -2
- package/dist/agent-fence.js +294 -11
- package/dist/agentconfig.js +4 -1
- package/dist/batch-checks.js +3 -2
- package/dist/browser/THIRD_PARTY_NOTICES.txt +46 -0
- package/dist/browser/terminal.js +134 -23
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +31 -29
- package/dist/builder.d.ts +4 -0
- package/dist/builder.js +56 -37
- 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/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 +18 -3
- package/dist/invoke.js +142 -17
- 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.d.ts +24 -13
- package/dist/live-terminal.js +81 -43
- 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 +2 -0
- package/dist/operate.js +436 -745
- 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 +6 -0
- package/dist/project-delete-ui.js +20 -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 +9 -0
- package/dist/release-adapters.js +81 -4
- package/dist/release.d.ts +7 -1
- package/dist/release.js +20 -7
- 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 +4 -8
- 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 +13 -0
- package/dist/scope.js +11 -18
- package/dist/scout-browser.js +2 -1
- package/dist/serve.js +3 -2
- package/dist/server/chrome.d.ts +10 -1
- package/dist/server/chrome.js +45 -4
- package/dist/server/console.css +60 -137
- package/dist/server/flows.js +2 -2
- package/dist/server/http.d.ts +2 -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-pages.d.ts +28 -3
- package/dist/server/render-pages.js +78 -9
- package/dist/server/render-results.d.ts +0 -6
- package/dist/server/render-results.js +3 -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 +43 -22
- package/dist/server/task-views.js +1 -1
- package/dist/server/tasks.js +10 -7
- 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 +11 -74
- package/dist/store.js +36 -179
- 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/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/teardown-git.d.ts +22 -0
- package/dist/teardown-git.js +136 -2
- 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-broker.d.ts +12 -0
- package/dist/tool-broker.js +66 -2
- 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/unfenced-test-support.d.ts +2 -0
- package/dist/unfenced-test-support.js +11 -0
- 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/dist/writes-mode.d.ts +9 -0
- package/dist/writes-mode.js +8 -0
- package/package.json +5 -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/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/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
|
package/dist/task-sizing.js
CHANGED
|
@@ -15,15 +15,14 @@
|
|
|
15
15
|
* The task text is data to the classifier, never instructions, and all it
|
|
16
16
|
* can return is one of three sizes, a yes/no, and a short reason.
|
|
17
17
|
*/
|
|
18
|
-
import { Buffer } from "node:buffer";
|
|
19
18
|
import { randomUUID } from "node:crypto";
|
|
20
19
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
21
20
|
import { tmpdir } from "node:os";
|
|
22
21
|
import { join } from "node:path";
|
|
23
22
|
import { readSizeAnswer, SIZING_MODEL_SCHEMA } from "./contracts/task-sizing.js";
|
|
24
|
-
import { strictJsonParse } from "./converse.js";
|
|
25
23
|
import { redactSecretLines, scanForSecrets } from "./evidence.js";
|
|
26
24
|
import { run, terminateOwnedProcesses } from "./exec.js";
|
|
25
|
+
import { CLAUDE_NO_TOOLS_ARGS, claudeJsonBody } from "./claude-print.js";
|
|
27
26
|
import { JEV_MODEL, JEV_URL } from "./flow-sort.js";
|
|
28
27
|
import { readProviderKey } from "./keys.js";
|
|
29
28
|
import {} from "./phase-routing.js";
|
|
@@ -93,13 +92,11 @@ export function claudeSizer(runner = run, model = SIZING_CLAUDE_MODEL, budgetMs
|
|
|
93
92
|
sizingText(input),
|
|
94
93
|
].join("\n");
|
|
95
94
|
const result = await runner("claude", [
|
|
96
|
-
"-p", "--output-format", "json", "--json-schema", JSON.stringify(SIZING_MODEL_SCHEMA),
|
|
97
|
-
"--permission-prompts", "none", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--model", model,
|
|
95
|
+
"-p", "--output-format", "json", "--json-schema", JSON.stringify(SIZING_MODEL_SCHEMA), ...CLAUDE_NO_TOOLS_ARGS, "--model", model,
|
|
98
96
|
], { cwd: dir, stdin: prompt, timeoutMs: budgetMs, maxBuffer: 256 * 1024, omitEnv: ALL_CREDENTIAL_ENV, processGroup: true, owner, beforeSpawn: () => !signal.aborted });
|
|
99
97
|
if (signal.aborted || result.notFound || result.timedOut || result.code !== 0)
|
|
100
98
|
return null;
|
|
101
|
-
const
|
|
102
|
-
const body = parsed.ok && typeof parsed.value === "object" && parsed.value !== null && !Array.isArray(parsed.value) ? parsed.value : null;
|
|
99
|
+
const body = claudeJsonBody(result.stdout, 256 * 1024);
|
|
103
100
|
if (body === null || body["is_error"] === true || body["subtype"] !== "success")
|
|
104
101
|
return null;
|
|
105
102
|
return answerOf(body["structured_output"]);
|
|
@@ -184,10 +181,6 @@ export function installFilingSizer(sizer, budgetMs = SIZING_BUDGET_MS) {
|
|
|
184
181
|
return () => { installed = previous; budget = previousBudget; };
|
|
185
182
|
}
|
|
186
183
|
let budget = SIZING_BUDGET_MS;
|
|
187
|
-
/** Whether filings in this process are classified. */
|
|
188
|
-
export function filingSizerInstalled() {
|
|
189
|
-
return installed !== null;
|
|
190
|
-
}
|
|
191
184
|
/**
|
|
192
185
|
* After a filing: ask the classifier (within the budget) and apply its size
|
|
193
186
|
* to the still-unapproved task. Runs in the background; the filing already
|
package/dist/teams-chat.js
CHANGED
|
@@ -6,12 +6,11 @@ import { chatAskButtons } from "./chat-ask.js";
|
|
|
6
6
|
import { ChatDeliveryError, ChatState, chatHash, partContent } from "./chat-delivery-state.js";
|
|
7
7
|
import { channelAccess, chatObject as object, heldForWatch, planChatNotifications, planRoomMessages, processChatEvent, splitChatText } from "./chat-delivery.js";
|
|
8
8
|
import { PLATFORM_LIMITS } from "./text-limits.js";
|
|
9
|
-
import { roomCommand } from "./chat-rooms.js";
|
|
10
9
|
import { LEAD_MESSAGE_MAX_CHARS } from "./lead.js";
|
|
11
10
|
import { renderReply } from "./reply-shape.js";
|
|
12
11
|
import { armedCardText, armedYesLabel, proposalLink, proposalOutcomeText, proposalPreview } from "./chat-channel.js";
|
|
13
12
|
import { chatFlowButtons } from "./chat-flow.js";
|
|
14
|
-
import {
|
|
13
|
+
import { admitChatEvent } from "./chat-admission.js";
|
|
15
14
|
import { chatResultHref } from "./chat-controls.js";
|
|
16
15
|
import { TeamsError } from "./teams-api.js";
|
|
17
16
|
import { readTeamsSubmit } from "./contracts/teams-callback.js";
|
|
@@ -113,16 +112,10 @@ export function receiveTeams(state, identity, raw, serviceUrlClaim, now) {
|
|
|
113
112
|
...(Array.isArray(activity.attachments) && activity.attachments.length > 0 ? { unsupported: "Incoming files are not supported yet. Describe the request in a message; saved result screenshots open from their links." } : {}) };
|
|
114
113
|
eventId = chatHash(`${identity.installation}:message:${id}`);
|
|
115
114
|
}
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
const inbox = isRoom && kind === "message" ? channelInbox(state.store, "teams", identity.installation, channel, words) : { watched: false, command: false };
|
|
119
|
-
const roomish = isRoom && (state.room(identity.installation, channel) !== null || (kind === "message" && roomCommand(words) !== null) || inbox.watched || inbox.command);
|
|
120
|
-
if (isRoom && !roomish)
|
|
121
|
-
return false;
|
|
122
|
-
const binding = state.bindingFor(identity.installation, member);
|
|
123
|
-
const open = inbox.watched && !inbox.command;
|
|
124
|
-
if (kind === "pair" ? !!binding || isRoom : !open && (!binding || !state.live(binding) || (binding.channel !== channel && !roomish)))
|
|
115
|
+
const admitted = admitChatEvent(state, "teams", identity.installation, { kind, member, channel, isRoom, payload });
|
|
116
|
+
if (admitted === null)
|
|
125
117
|
return false;
|
|
118
|
+
const { binding } = admitted;
|
|
126
119
|
return state.store.transact(() => {
|
|
127
120
|
state.setMeta(identity.installation, serviceKey(channel), activity.serviceUrl, now);
|
|
128
121
|
return state.enqueue({
|
package/dist/teams.js
CHANGED
|
@@ -4,7 +4,7 @@ import { adapterPolicy } from "./server/route-policy.js";
|
|
|
4
4
|
* has no client-initiated socket, so inbound activities arrive over HTTPS
|
|
5
5
|
* and everything else runs from the database like the other channels. */
|
|
6
6
|
import { randomBytes } from "node:crypto";
|
|
7
|
-
import {
|
|
7
|
+
import { abortablePause, workChatQueue } from "./chat-follow.js";
|
|
8
8
|
import { ChatState } from "./chat-delivery-state.js";
|
|
9
9
|
import { chatObject as object } from "./chat-delivery.js";
|
|
10
10
|
import { loadTeamsCredentials, teamsAccessToken, teamsApi, verifyTeamsToken, TeamsError } from "./teams-api.js";
|
|
@@ -78,10 +78,7 @@ export async function handleTeamsHttp(request, response, options) {
|
|
|
78
78
|
/** The worker-side loop: a lease per installation, one pass a second. */
|
|
79
79
|
export async function followTeams(options) {
|
|
80
80
|
const owner = randomBytes(16).toString("hex"), state = new ChatState(options.store, "teams");
|
|
81
|
-
const pause =
|
|
82
|
-
await sleep(ms, undefined, { signal: options.signal });
|
|
83
|
-
}
|
|
84
|
-
catch { /* shutdown */ } };
|
|
81
|
+
const pause = abortablePause(options.signal);
|
|
85
82
|
while (!options.signal.aborted) {
|
|
86
83
|
const credentials = loadTeamsCredentials(options.dir);
|
|
87
84
|
if (!credentials || !state.lease(credentials.installation, owner, new Date())) {
|
|
@@ -110,20 +107,16 @@ export async function followTeams(options) {
|
|
|
110
107
|
await pause(30_000);
|
|
111
108
|
continue;
|
|
112
109
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (same() && options.notifications())
|
|
124
|
-
await planTeamsNotifications(chat);
|
|
125
|
-
await pause(1000);
|
|
126
|
-
}
|
|
110
|
+
await workChatQueue({
|
|
111
|
+
same,
|
|
112
|
+
ready: () => !(state.retryAt(credentials.installation) > new Date().toISOString()),
|
|
113
|
+
pause,
|
|
114
|
+
notifications: () => options.notifications(),
|
|
115
|
+
process: () => processTeamsEvent(chat),
|
|
116
|
+
rooms: () => planTeamsRooms(chat),
|
|
117
|
+
deliver: () => deliverTeamsPart(chat),
|
|
118
|
+
notify: () => planTeamsNotifications(chat),
|
|
119
|
+
});
|
|
127
120
|
}
|
|
128
121
|
catch {
|
|
129
122
|
if (alive)
|
package/dist/teardown-git.d.ts
CHANGED
|
@@ -8,7 +8,29 @@ export declare const REFUSED_CODE = 128;
|
|
|
8
8
|
/** The command-running setting a `git config --list --show-scope -z` listing sets, or null. Only local and worktree
|
|
9
9
|
* scope count: teardown commands never read system or global config. */
|
|
10
10
|
export declare function commandSetting(listing: string): string | null;
|
|
11
|
+
/** The `-c` settings that carry the operator's own values (CARRIED_VALUES, LFS_FILTER) from a global/system listing. */
|
|
12
|
+
export declare function carriedSettings(listing: string): string[];
|
|
11
13
|
/** Why a teardown command was refused, or null when `result` isn't a refusal. */
|
|
12
14
|
export declare function refusal(result: ExecResult): string | null;
|
|
13
15
|
/** The one way teardown runs git, including the target checkout immediately before removal. */
|
|
14
16
|
export declare function teardownGit(runner?: Runner): Git;
|
|
17
|
+
/**
|
|
18
|
+
* Git on a build's checkout, as the plane runs it outside every fence (staging, committing and inspecting the build's
|
|
19
|
+
* work): the same fixed hardening as teardown (no hooks, fsmonitor, ext transport, credential helpers or automatic
|
|
20
|
+
* maintenance, no submodule recursion, no signing), refused outright when the checkout's own config includes a file
|
|
21
|
+
* or names a command — a filter, an external diff, a textconv, a pager — which a build could have planted. It reads no
|
|
22
|
+
* global or system config: only their values (identity, line endings, a standard git-lfs install) are carried over.
|
|
23
|
+
* It never enters a submodule or nested repository (whose config it never read).
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Whether a checkout's own git config (its repository's config and its worktree's config.worktree) includes another
|
|
27
|
+
* file, read without git: every git command follows includes as it starts, so an include of the build's could point
|
|
28
|
+
* git at any file, a pipe that never ends among them. Each file is read only as the regular file it is, bounded; one
|
|
29
|
+
* that can't be read so counts as including.
|
|
30
|
+
*/
|
|
31
|
+
export declare function checkoutIncludes(checkout: string): boolean;
|
|
32
|
+
/** Whether the checkout's .lfsconfig (read as git parses it, that file only) sets any git-lfs key that could name a
|
|
33
|
+
* program (anything but INERT_LFS). A file that
|
|
34
|
+
* isn't a plain one, or doesn't parse, counts as naming one. */
|
|
35
|
+
export declare function lfsExtensionIn(checkout: string): boolean;
|
|
36
|
+
export declare function checkoutGit(inner?: Runner): Runner;
|
package/dist/teardown-git.js
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* any of them the command never runs. The refusal is a failed git result, so every caller's existing doubt applies and
|
|
11
11
|
* nothing is removed.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { spawnSync } from "node:child_process";
|
|
14
|
+
import { existsSync, lstatSync, readdirSync, readFileSync } from "node:fs";
|
|
14
15
|
import { join, resolve } from "node:path";
|
|
15
16
|
import { run } from "./exec.js";
|
|
16
17
|
import { ENV, ENV_ALLOWLIST } from "./remote-main.js";
|
|
@@ -31,6 +32,10 @@ const COMMAND_KEYS = [
|
|
|
31
32
|
"diff.*.command", "diff.*.textconv", "merge.*.driver", "gpg.program", "gpg.*.program", "uploadpack.packobjectshook",
|
|
32
33
|
"remote.*.uploadpack", "remote.*.receivepack", "remote.*.vcs", "pager.*", "include.path", "includeif.*.path",
|
|
33
34
|
].map(key => new RegExp(`^${key.replace(/\./g, "\\.").replace(/\*/g, ".+")}$`));
|
|
35
|
+
/** git-lfs settings that name no program (where it fetches, what, whether it checks locks, how it signs in); any other
|
|
36
|
+
* `lfs.*` key in a checkout's own config could (extensions, custom and standalone transfer agents), so it's refused. */
|
|
37
|
+
const INERT_LFS = /^lfs\.(url|pushurl|fetchinclude|fetchexclude|locksverify|repositoryformatversion|.+\.access|.+\.locksverify|setlockablereadonly|skipdownloaderrors|concurrenttransfers)$/;
|
|
38
|
+
const lfsCommand = (key) => key.startsWith("lfs.") && !INERT_LFS.test(key);
|
|
34
39
|
/** core.fsmonitor as a boolean is git's own daemon; anything else is a command to run. */
|
|
35
40
|
const BOOLEAN = /^(|true|false|yes|no|on|off|-?\d+)$/i;
|
|
36
41
|
/** The command-running setting a `git config --list --show-scope -z` listing sets, or null. Only local and worktree
|
|
@@ -45,11 +50,40 @@ export function commandSetting(listing) {
|
|
|
45
50
|
const newline = entry.indexOf("\n");
|
|
46
51
|
const key = (newline < 0 ? entry : entry.slice(0, newline)).toLowerCase();
|
|
47
52
|
const value = newline < 0 ? "" : entry.slice(newline + 1);
|
|
48
|
-
if (key === "core.fsmonitor" ? !BOOLEAN.test(value.trim()) : COMMAND_KEYS.some(pattern => pattern.test(key)))
|
|
53
|
+
if (key === "core.fsmonitor" ? !BOOLEAN.test(value.trim()) : COMMAND_KEYS.some(pattern => pattern.test(key)) || lfsCommand(key))
|
|
49
54
|
return key;
|
|
50
55
|
}
|
|
51
56
|
return null;
|
|
52
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* What the plane's git on a build's checkout keeps of the operator's own (global and system) configuration: values,
|
|
60
|
+
* never commands — identity, line endings, ignore and case settings — and git-lfs's filters only as git-lfs itself
|
|
61
|
+
* installs them, exactly. Everything else there (filters, diff drivers, fsmonitor, signing and its key commands,
|
|
62
|
+
* whatever an include brings in) is left out, so nothing in it can run a file of the checkout's.
|
|
63
|
+
*/
|
|
64
|
+
const CARRIED_VALUES = new Set(["user.name", "user.email", "core.autocrlf", "core.eol", "core.safecrlf", "core.excludesfile", "core.ignorecase",
|
|
65
|
+
"core.precomposeunicode", "core.quotepath", "core.filemode", "core.symlinks", "core.longpaths"]);
|
|
66
|
+
const LFS_FILTER = {
|
|
67
|
+
"filter.lfs.clean": "git-lfs clean -- %f", "filter.lfs.smudge": "git-lfs smudge -- %f", "filter.lfs.process": "git-lfs filter-process", "filter.lfs.required": "true",
|
|
68
|
+
};
|
|
69
|
+
/** The `-c` settings that carry the operator's own values (CARRIED_VALUES, LFS_FILTER) from a global/system listing. */
|
|
70
|
+
export function carriedSettings(listing) {
|
|
71
|
+
const fields = listing.split("\0");
|
|
72
|
+
const carried = new Map();
|
|
73
|
+
for (let at = 0; at + 1 < fields.length; at += 2) {
|
|
74
|
+
if (fields[at] !== "global" && fields[at] !== "system")
|
|
75
|
+
continue;
|
|
76
|
+
const entry = fields[at + 1];
|
|
77
|
+
const newline = entry.indexOf("\n");
|
|
78
|
+
if (newline < 0)
|
|
79
|
+
continue;
|
|
80
|
+
const key = entry.slice(0, newline).toLowerCase();
|
|
81
|
+
const value = entry.slice(newline + 1);
|
|
82
|
+
if (CARRIED_VALUES.has(key) || LFS_FILTER[key] === value)
|
|
83
|
+
carried.set(key, value);
|
|
84
|
+
}
|
|
85
|
+
return [...carried].flatMap(([key, value]) => ["-c", `${key}=${value}`]);
|
|
86
|
+
}
|
|
53
87
|
/** Why a teardown command was refused, or null when `result` isn't a refusal. */
|
|
54
88
|
export function refusal(result) {
|
|
55
89
|
return result.code === REFUSED_CODE && result.stderr.startsWith(REFUSED_PREFIX) ? result.stderr.slice(REFUSED_PREFIX.length).trim() : null;
|
|
@@ -117,3 +151,103 @@ export function teardownGit(runner = run) {
|
|
|
117
151
|
return runner("git", [...TEARDOWN_FLAGS, ...safeArgs], options(cwd));
|
|
118
152
|
};
|
|
119
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Git on a build's checkout, as the plane runs it outside every fence (staging, committing and inspecting the build's
|
|
156
|
+
* work): the same fixed hardening as teardown (no hooks, fsmonitor, ext transport, credential helpers or automatic
|
|
157
|
+
* maintenance, no submodule recursion, no signing), refused outright when the checkout's own config includes a file
|
|
158
|
+
* or names a command — a filter, an external diff, a textconv, a pager — which a build could have planted. It reads no
|
|
159
|
+
* global or system config: only their values (identity, line endings, a standard git-lfs install) are carried over.
|
|
160
|
+
* It never enters a submodule or nested repository (whose config it never read).
|
|
161
|
+
*/
|
|
162
|
+
/**
|
|
163
|
+
* Whether a checkout's own git config (its repository's config and its worktree's config.worktree) includes another
|
|
164
|
+
* file, read without git: every git command follows includes as it starts, so an include of the build's could point
|
|
165
|
+
* git at any file, a pipe that never ends among them. Each file is read only as the regular file it is, bounded; one
|
|
166
|
+
* that can't be read so counts as including.
|
|
167
|
+
*/
|
|
168
|
+
export function checkoutIncludes(checkout) {
|
|
169
|
+
const regular = (path) => {
|
|
170
|
+
try {
|
|
171
|
+
const held = lstatSync(path);
|
|
172
|
+
if (!held.isFile() || held.size > 1024 * 1024)
|
|
173
|
+
return null;
|
|
174
|
+
return readFileSync(path, "utf8");
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
let gitdir = join(checkout, ".git");
|
|
181
|
+
try {
|
|
182
|
+
if (!lstatSync(gitdir).isDirectory()) {
|
|
183
|
+
// As git reads a .git file: "gitdir: " then the path, one line; anything else (more lines, stray space) refused.
|
|
184
|
+
const text = (regular(gitdir) ?? "").replace(/\r?\n$/, "");
|
|
185
|
+
if (!text.startsWith("gitdir: ") || /[\r\n]/.test(text))
|
|
186
|
+
return true;
|
|
187
|
+
const pointer = text.slice("gitdir: ".length);
|
|
188
|
+
if (pointer === "" || pointer !== pointer.trim())
|
|
189
|
+
return true;
|
|
190
|
+
gitdir = resolve(checkout, pointer);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
const common = (() => { const named = regular(join(gitdir, "commondir"))?.trim(); return named === undefined || named === "" ? gitdir : resolve(gitdir, named); })();
|
|
197
|
+
for (const path of [join(common, "config"), join(gitdir, "config.worktree")]) {
|
|
198
|
+
if (!existsSync(path) && path.endsWith("config.worktree"))
|
|
199
|
+
continue;
|
|
200
|
+
const text = regular(path);
|
|
201
|
+
if (text === null || /^\s*\[\s*include(if)?\b/im.test(text))
|
|
202
|
+
return true;
|
|
203
|
+
// And as git itself parses it, includes not followed (only this one regular file, from a neutral folder, with no
|
|
204
|
+
// other config read): an include git sees by any spelling is refused.
|
|
205
|
+
const parsed = spawnSync("git", ["config", "--file", path, "--no-includes", "--name-only", "--list", "-z"], { cwd: "/", encoding: "utf8", timeout: 5_000, env: { PATH: process.env["PATH"] ?? "", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", HOME: "/nonexistent" } });
|
|
206
|
+
if (parsed.status !== 0 || parsed.stdout.split("\0").some(name => /^(include\.path|includeif\..*\.path)$/i.test(name)))
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
/** Whether the checkout's .lfsconfig (read as git parses it, that file only) sets any git-lfs key that could name a
|
|
212
|
+
* program (anything but INERT_LFS). A file that
|
|
213
|
+
* isn't a plain one, or doesn't parse, counts as naming one. */
|
|
214
|
+
export function lfsExtensionIn(checkout) {
|
|
215
|
+
const path = join(checkout, ".lfsconfig");
|
|
216
|
+
try {
|
|
217
|
+
if (!lstatSync(path).isFile())
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
const parsed = spawnSync("git", ["config", "--file", path, "--no-includes", "--name-only", "--list", "-z"], { cwd: "/", encoding: "utf8", timeout: 5_000, env: { PATH: process.env["PATH"] ?? "", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", HOME: "/nonexistent" } });
|
|
224
|
+
return parsed.status !== 0 || parsed.stdout.split("\0").filter(Boolean).some(name => lfsCommand(name.toLowerCase()) || /^include(if)?\./i.test(name));
|
|
225
|
+
}
|
|
226
|
+
export function checkoutGit(inner = run) {
|
|
227
|
+
return async (file, args, options) => {
|
|
228
|
+
if (file !== "git")
|
|
229
|
+
return inner(file, args, options);
|
|
230
|
+
const cwd = options?.cwd;
|
|
231
|
+
if (cwd === undefined || checkoutIncludes(cwd))
|
|
232
|
+
return refused("the checkout's git config includes another file, or couldn't be read as a plain file");
|
|
233
|
+
// git-lfs also reads .lfsconfig from the checkout: one that names an extension (a command it would run) is refused.
|
|
234
|
+
if (lfsExtensionIn(cwd))
|
|
235
|
+
return refused("the checkout's .lfsconfig sets a git-lfs key that can run a command");
|
|
236
|
+
// The checkout's own config may name no command.
|
|
237
|
+
const own = await inner("git", ["--no-optional-locks", "config", "--list", "--show-scope", "-z"], options);
|
|
238
|
+
if (own.code !== 0)
|
|
239
|
+
return refused("its config couldn't be read");
|
|
240
|
+
const key = commandSetting(own.stdout);
|
|
241
|
+
if (key !== null)
|
|
242
|
+
return refused(`the checkout's git config sets ${key}, which can run a command`);
|
|
243
|
+
// The operator's own config contributes values only (carriedSettings); git then reads no global or system config.
|
|
244
|
+
const carried = carriedSettings(own.stdout);
|
|
245
|
+
const env = { ...(options?.env ?? {}), GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null" };
|
|
246
|
+
// Never into a submodule or nested repository, whose own config this never read: status and diff skip them.
|
|
247
|
+
const safeArgs = [...args];
|
|
248
|
+
const command = safeArgs.findIndex(arg => arg === "status" || arg === "diff");
|
|
249
|
+
if (command >= 0 && !safeArgs.includes("--ignore-submodules=all"))
|
|
250
|
+
safeArgs.splice(command + 1, 0, "--ignore-submodules=all");
|
|
251
|
+
return inner("git", [...TEARDOWN_FLAGS, "-c", "diff.ignoreSubmodules=all", "-c", "commit.gpgsign=false", "-c", "tag.gpgsign=false", ...carried, ...safeArgs], { ...options, env });
|
|
252
|
+
};
|
|
253
|
+
}
|
package/dist/telegram-lead.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CHAT_ACTIONS, sharedActionAllowsChallenge, sharedActionPayload } from "./chat-actions.js";
|
|
2
|
-
import { taskInCeiling, mirrorToTaskChat, couldNotAnswerText, channelRepos as telegramConversationRepos, resolveChannelLead as resolveTelegramLead,
|
|
2
|
+
import { taskInCeiling, mirrorToTaskChat, couldNotAnswerText, channelRepos as telegramConversationRepos, resolveChannelLead as resolveTelegramLead, NO_PHONE_LINK, NO_TASK_LINK, proposalLink, confirmedLink, proposalPreview, proposalOutcomeText, handoffCardText, confirmedCardText } from "./chat-channel.js";
|
|
3
3
|
export { channelRepos as telegramConversationRepos, resolveChannelLead as resolveTelegramLead, tooLongText, whichTaskText, replyContextFor, NO_PHONE_LINK, NO_TASK_LINK, proposalLink, confirmedLink, proposalPreview, proposalOutcomeText, handoffCardText, confirmedCardText, CHAT_ACTION_PARITY as TELEGRAM_ACTION_PARITY, parityGaps } from "./chat-channel.js";
|
|
4
4
|
/**
|
|
5
5
|
* The paired phone as one more way to use the same assistant.
|
|
@@ -23,7 +23,7 @@ export { channelRepos as telegramConversationRepos, resolveChannelLead as resolv
|
|
|
23
23
|
* the console and CLI state), and the door re-proves everything again.
|
|
24
24
|
*/
|
|
25
25
|
import { createHash, randomBytes } from "node:crypto";
|
|
26
|
-
import {
|
|
26
|
+
import { leadFailureText, runLeadTurn } from "./lead.js";
|
|
27
27
|
import { confirmLeadProposal, dismissLeadProposal } from "./lead-doors.js";
|
|
28
28
|
import { ceilingDigestOf, verifyApproverStanding } from "./principal.js";
|
|
29
29
|
import { LEAD_ASK_OTHER } from "./store.js";
|
package/dist/telegram.js
CHANGED
|
@@ -27,7 +27,7 @@ import { chmodSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
|
27
27
|
import { join } from "node:path";
|
|
28
28
|
import { closedQuestionWords, validateNote } from "./decision.js";
|
|
29
29
|
import { isShotsKind, resultShotsFor } from "./result-shots.js";
|
|
30
|
-
import { isLifecycleNotification, isTelegramProgressNotification,
|
|
30
|
+
import { isLifecycleNotification, isTelegramProgressNotification, TELEGRAM_HOLD_REASONS } from "./store.js";
|
|
31
31
|
import { telegramProgressCard } from "./telegram-progress.js";
|
|
32
32
|
import { enqueueEveningDigests, finishedView, isTaskFact, joinsBatch, needsPerson, quietCardView } from "./chat-quiet.js";
|
|
33
33
|
import { BATCH_MS, chatText, chatTitle, factLinkLabel, mentions, nameTelegramBot } from "./chat-voice.js";
|
package/dist/token-notices.d.ts
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* API token notices (v111): 7 days and 1 day before a token expires, its person is told once each, in their own chats
|
|
3
|
-
* and browsers (a personal notification), and the ledger keeps it. Only the nearest notice newly due is sent, so a
|
|
4
|
-
* token made with a day left says so once. Replaced and revoked tokens get none. The same pass records the end of each
|
|
5
|
-
* rotated token whose overlap has passed; it already signs no one in (tokenLive), so a late pass grants nothing.
|
|
6
|
-
*/
|
|
7
|
-
import { tokenLive } from "./api-tokens.js";
|
|
8
1
|
import type { Store } from "./store.js";
|
|
9
2
|
export declare const TOKEN_NOTICE_KIND = "api-token-expiry";
|
|
10
3
|
export declare function tokenNoticePass(store: Store, now: Date): {
|
|
@@ -14,9 +7,5 @@ export declare function tokenNoticePass(store: Store, now: Date): {
|
|
|
14
7
|
}[];
|
|
15
8
|
ended: number;
|
|
16
9
|
};
|
|
17
|
-
/** Whether a token's console card warns that it expires soon (within the first notice's days). */
|
|
18
|
-
export declare function expiresSoon(token: Parameters<typeof tokenLive>[0] & {
|
|
19
|
-
replacedBy: string | null;
|
|
20
|
-
}, now: number): boolean;
|
|
21
10
|
/** The loop beside the console: a pass a minute. */
|
|
22
11
|
export declare function startTokenNotices(store: Store, everyMs?: number): () => void;
|
package/dist/token-notices.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* token made with a day left says so once. Replaced and revoked tokens get none. The same pass records the end of each
|
|
5
5
|
* rotated token whose overlap has passed; it already signs no one in (tokenLive), so a late pass grants nothing.
|
|
6
6
|
*/
|
|
7
|
-
import { expiryNoticeDue, EXPIRY_NOTICE_DAYS
|
|
7
|
+
import { expiryNoticeDue, EXPIRY_NOTICE_DAYS } from "./api-tokens.js";
|
|
8
8
|
export const TOKEN_NOTICE_KIND = "api-token-expiry";
|
|
9
9
|
export function tokenNoticePass(store, now) {
|
|
10
10
|
const ended = store.endRotatedApiTokens(now);
|
|
@@ -32,10 +32,6 @@ export function tokenNoticePass(store, now) {
|
|
|
32
32
|
}
|
|
33
33
|
return { sent, ended };
|
|
34
34
|
}
|
|
35
|
-
/** Whether a token's console card warns that it expires soon (within the first notice's days). */
|
|
36
|
-
export function expiresSoon(token, now) {
|
|
37
|
-
return expiryNoticeDue(token, now) !== null;
|
|
38
|
-
}
|
|
39
35
|
/** The loop beside the console: a pass a minute. */
|
|
40
36
|
export function startTokenNotices(store, everyMs = 60_000) {
|
|
41
37
|
let stopped = false;
|
package/dist/tool-broker.d.ts
CHANGED
|
@@ -11,7 +11,13 @@ export type BrokerOptions = {
|
|
|
11
11
|
omitEnv?: readonly string[];
|
|
12
12
|
/** Toolroll's own settings for a local server (the run's isolated database), passed as they are. */
|
|
13
13
|
pass?: Readonly<Record<string, string>>;
|
|
14
|
+
/** Where a local server starts: a folder the build can't write (never its checkout). */
|
|
14
15
|
cwd?: string;
|
|
16
|
+
/** Folders the build writes (its checkout, its caches): a server whose command, or a path it's given, resolves inside
|
|
17
|
+
* one is never started, since it would run the build's own bytes outside the build's limits. */
|
|
18
|
+
untrusted?: readonly string[];
|
|
19
|
+
/** Why a local server may not start unwrapped (no fence for it here), read when it would; null when it may. */
|
|
20
|
+
unwrapped?: () => string | null;
|
|
15
21
|
/** How a local server is started inside its run's fence, read when it starts (the fence is fixed after the tools). */
|
|
16
22
|
wrap?: () => ((file: string, args: readonly string[]) => {
|
|
17
23
|
file: string;
|
|
@@ -35,5 +41,11 @@ export declare const SERVER_ENV: readonly string[];
|
|
|
35
41
|
/** A local server's environment: the allowlisted names from `base` (and its LC_* locale), less `omit`, then `pass`,
|
|
36
42
|
* then the tool's own values. */
|
|
37
43
|
export declare function serverEnv(base: NodeJS.ProcessEnv, omit: readonly string[], pass: Readonly<Record<string, string>>, values: Readonly<Record<string, string>>): NodeJS.ProcessEnv;
|
|
44
|
+
/**
|
|
45
|
+
* The first of a server's command and path-like arguments that resolves inside an untrusted folder (a build's checkout
|
|
46
|
+
* or caches), or null. The command is resolved as spawn would: a path against `cwd`, a bare name through PATH; an
|
|
47
|
+
* argument counts when it names an existing path (relative to `cwd`) or is written as one (`./x`, `../x`, `/x`).
|
|
48
|
+
*/
|
|
49
|
+
export declare function workspaceBytes(command: string, args: readonly string[], cwd: string | undefined, path: string | undefined, untrusted: readonly string[]): string | null;
|
|
38
50
|
/** Start one launch's broker on a loopback port. */
|
|
39
51
|
export declare function startToolBroker(tools: readonly BrokeredTool[], options?: BrokerOptions): Promise<ToolBroker>;
|