toolroll 0.9.63 → 0.9.67
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 +108 -0
- package/README.md +33 -5
- package/dist/acp-agents.d.ts +43 -0
- package/dist/acp-agents.js +62 -0
- package/dist/acp-attest.d.ts +44 -0
- package/dist/acp-attest.js +269 -0
- package/dist/acp-check.d.ts +28 -0
- package/dist/acp-check.js +410 -0
- package/dist/acp-client.d.ts +163 -0
- package/dist/acp-client.js +738 -0
- package/dist/acp-inspect.d.ts +23 -0
- package/dist/acp-inspect.js +109 -0
- package/dist/agent-board.d.ts +19 -6
- package/dist/agent-board.js +30 -9
- package/dist/agent-fence.d.ts +117 -2
- package/dist/agent-fence.js +439 -12
- package/dist/agent-pulse.js +9 -2
- package/dist/agent-state-words.d.ts +19 -0
- package/dist/agent-state-words.js +20 -0
- package/dist/agent-state.d.ts +65 -0
- package/dist/agent-state.js +229 -0
- package/dist/agentconfig.js +39 -13
- package/dist/agents-board.d.ts +34 -2
- package/dist/agents-board.js +110 -1
- package/dist/approval-rules-ui.js +2 -2
- package/dist/assignment-delivery.d.ts +14 -0
- package/dist/assignment-delivery.js +13 -0
- package/dist/assignment.js +13 -0
- package/dist/attest.js +7 -0
- package/dist/backup-ui.js +2 -2
- package/dist/batch-checks.js +3 -2
- package/dist/browser/THIRD_PARTY_NOTICES.txt +94 -0
- package/dist/browser/terminal.css +37 -0
- package/dist/browser/terminal.js +228 -0
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +42 -38
- package/dist/browser-shell.js +6 -3
- package/dist/browser-workspace.d.ts +235 -1
- package/dist/browser-workspace.js +60 -0
- package/dist/builder.d.ts +10 -1
- package/dist/builder.js +87 -24
- package/dist/chat-actions.d.ts +13 -1
- package/dist/chat-actions.js +197 -15
- package/dist/chat-ask.d.ts +4 -0
- package/dist/chat-ask.js +14 -0
- package/dist/chat-channel.js +20 -0
- package/dist/chat-continuity.js +5 -4
- package/dist/chat-decide.d.ts +0 -14
- package/dist/chat-decide.js +4 -1
- package/dist/chat-delivery.d.ts +9 -0
- package/dist/chat-delivery.js +45 -6
- package/dist/chat-messages.d.ts +8 -0
- package/dist/chat-messages.js +36 -4
- package/dist/check-levels-ui.js +2 -2
- package/dist/checkout-proof.d.ts +28 -0
- package/dist/checkout-proof.js +62 -0
- package/dist/claim.d.ts +12 -0
- package/dist/claim.js +19 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +4 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +21 -2
- package/dist/coding-ui.js +6 -4
- package/dist/console-instance.d.ts +124 -0
- package/dist/console-instance.js +337 -0
- package/dist/contracts/chat-actions.d.ts +16 -0
- package/dist/contracts/chat-actions.js +4 -0
- package/dist/contracts/chat-content.d.ts +19 -0
- package/dist/contracts/chat-content.js +6 -0
- package/dist/contracts/console-api.d.ts +13 -4
- package/dist/contracts/console-api.js +13 -4
- package/dist/contracts/flow-card.d.ts +4 -0
- package/dist/contracts/flow-card.js +3 -0
- package/dist/contracts/flow.d.ts +46 -0
- package/dist/contracts/flow.js +11 -2
- package/dist/contracts/lead-tools.d.ts +92 -0
- package/dist/contracts/lead-tools.js +13 -0
- package/dist/contracts/provider.d.ts +11 -1
- package/dist/contracts/provider.js +1 -1
- package/dist/contracts/route.d.ts +76 -1
- package/dist/contracts/route.js +1 -1
- package/dist/contracts/store-json.d.ts +2 -0
- package/dist/control-setup.d.ts +1 -1
- package/dist/control-ui.js +2 -2
- package/dist/converse.d.ts +16 -0
- package/dist/converse.js +32 -4
- package/dist/demo-chat.js +4 -4
- package/dist/demo.js +2 -2
- package/dist/desktop-update-gate.js +6 -0
- package/dist/desktop-update.d.ts +1 -1
- package/dist/discord-chat.js +4 -1
- package/dist/doctor.js +1 -1
- package/dist/egress-bridge.d.ts +17 -0
- package/dist/egress-bridge.js +111 -0
- package/dist/egress.d.ts +119 -0
- package/dist/egress.js +423 -0
- package/dist/exec.d.ts +78 -0
- package/dist/exec.js +367 -9
- package/dist/export-ui.js +2 -2
- package/dist/flow-engine.js +24 -5
- package/dist/flow-gallery-ui.js +2 -2
- package/dist/flow-gallery.js +27 -8
- package/dist/flow-share.js +16 -5
- package/dist/flow-triggers.d.ts +77 -8
- package/dist/flow-triggers.js +517 -70
- package/dist/flows-cli.js +8 -2
- package/dist/flows-ui.d.ts +0 -2
- package/dist/flows-ui.js +52 -4
- package/dist/flows.d.ts +4 -0
- package/dist/flows.js +9 -1
- package/dist/html.d.ts +9 -0
- package/dist/html.js +11 -0
- package/dist/invoke.d.ts +35 -0
- package/dist/invoke.js +347 -28
- package/dist/keys.d.ts +18 -14
- package/dist/keys.js +5 -0
- package/dist/lead-capabilities.js +3 -2
- package/dist/lead-contract.js +2 -0
- package/dist/lead-doors.d.ts +2 -0
- package/dist/lead-doors.js +7 -0
- package/dist/lead-follow.d.ts +2 -0
- package/dist/lead-follow.js +58 -2
- package/dist/lead-progress.js +1 -0
- package/dist/lead-queue-runtime.d.ts +46 -0
- package/dist/lead-queue-runtime.js +191 -0
- package/dist/lead-queue.d.ts +104 -0
- package/dist/lead-queue.js +187 -0
- package/dist/lead-sweep.d.ts +67 -0
- package/dist/lead-sweep.js +99 -0
- package/dist/lead-tools.d.ts +22 -0
- package/dist/lead-tools.js +152 -1
- package/dist/lead.d.ts +13 -5
- package/dist/lead.js +89 -29
- package/dist/limits-ui.d.ts +8 -1
- package/dist/limits-ui.js +26 -0
- package/dist/live-bus.d.ts +8 -0
- package/dist/live-bus.js +40 -4
- package/dist/live-detail.d.ts +27 -0
- package/dist/live-detail.js +57 -0
- package/dist/live-notes.d.ts +20 -0
- package/dist/live-notes.js +47 -0
- package/dist/live-records.d.ts +140 -0
- package/dist/live-records.js +935 -0
- package/dist/live-terminal.d.ts +269 -0
- package/dist/live-terminal.js +686 -0
- package/dist/live.d.ts +38 -27
- package/dist/live.js +142 -124
- package/dist/mcp-oauth.d.ts +6 -3
- package/dist/mcp-oauth.js +8 -4
- package/dist/memory-pass.js +2 -2
- package/dist/mobile-viewport.d.ts +5 -1
- package/dist/mobile-viewport.js +18 -2
- package/dist/model-catalog.js +4 -0
- package/dist/monitoring-ui.js +2 -2
- package/dist/needs-you.d.ts +1 -1
- package/dist/needs-you.js +11 -4
- package/dist/operate.d.ts +4 -0
- package/dist/operate.js +335 -19
- package/dist/passwordless.d.ts +23 -0
- package/dist/passwordless.js +26 -0
- package/dist/peek-cli.d.ts +12 -4
- package/dist/peek-cli.js +19 -11
- package/dist/phase-routing.js +10 -3
- package/dist/planner-source.d.ts +3 -0
- package/dist/planner-source.js +14 -0
- package/dist/planner.d.ts +2 -1
- package/dist/planner.js +7 -1
- package/dist/policy-ui.js +2 -2
- package/dist/policy.d.ts +1 -20
- package/dist/policy.js +29 -2
- package/dist/project-delete-ui.d.ts +18 -0
- package/dist/project-delete-ui.js +60 -9
- package/dist/project-delete.js +2 -2
- package/dist/project-memory.d.ts +3 -1
- package/dist/project-memory.js +57 -25
- package/dist/project-tools.d.ts +5 -3
- package/dist/project-tools.js +12 -9
- package/dist/provider-auth.d.ts +4 -3
- package/dist/provider-auth.js +5 -4
- package/dist/provider-connection.d.ts +2 -1
- package/dist/provider-connection.js +5 -0
- package/dist/provider.d.ts +25 -2
- package/dist/provider.js +63 -9
- package/dist/pull-request-ui.js +2 -2
- package/dist/push.js +1 -1
- package/dist/quick-start.d.ts +24 -0
- package/dist/quick-start.js +127 -0
- package/dist/release-adapters.d.ts +6 -0
- package/dist/release-adapters.js +70 -3
- package/dist/release.d.ts +10 -1
- package/dist/release.js +34 -15
- package/dist/request-budget-ui.js +2 -2
- package/dist/result-follow-ups.js +3 -2
- package/dist/result-review.d.ts +1 -1
- package/dist/result-review.js +9 -3
- package/dist/retention-ui.js +2 -2
- package/dist/retention.js +10 -1
- package/dist/run-efficiency.d.ts +73 -0
- package/dist/run-efficiency.js +228 -0
- package/dist/runner.d.ts +5 -1
- package/dist/runner.js +22 -1
- package/dist/scope.d.ts +183 -5
- package/dist/scope.js +256 -31
- package/dist/scout-net.d.ts +4 -1
- package/dist/scout-net.js +37 -12
- package/dist/scout.d.ts +20 -6
- package/dist/scout.js +27 -3
- package/dist/serve.js +248 -40
- package/dist/server/chat.d.ts +1 -1
- package/dist/server/chat.js +122 -31
- package/dist/server/chrome.d.ts +16 -2
- package/dist/server/chrome.js +63 -11
- package/dist/server/console.css +107 -42
- package/dist/server/flows.js +38 -8
- package/dist/server/guards.d.ts +1 -0
- package/dist/server/guards.js +23 -9
- package/dist/server/http.d.ts +7 -0
- package/dist/server/http.js +3 -2
- package/dist/server/live.d.ts +34 -1
- package/dist/server/live.js +39 -17
- package/dist/server/pages-assets.js +5 -0
- package/dist/server/pages.js +90 -13
- package/dist/server/people-tokens.d.ts +3 -0
- package/dist/server/people-tokens.js +55 -17
- package/dist/server/remote-hooks.js +12 -6
- package/dist/server/remote.d.ts +2 -1
- package/dist/server/remote.js +14 -5
- package/dist/server/render-chat.d.ts +12 -3
- package/dist/server/render-chat.js +47 -16
- package/dist/server/render-pages.d.ts +32 -4
- package/dist/server/render-pages.js +83 -13
- package/dist/server/render-people.d.ts +3 -2
- package/dist/server/render-people.js +3 -2
- package/dist/server/render-results.d.ts +18 -2
- package/dist/server/render-results.js +37 -28
- package/dist/server/render-settings.js +6 -5
- package/dist/server/render-tasks.d.ts +35 -10
- package/dist/server/render-tasks.js +111 -27
- package/dist/server/request-context.d.ts +2 -0
- package/dist/server/request-context.js +2 -1
- package/dist/server/route-table.d.ts +3 -0
- package/dist/server/route-table.js +47 -5
- package/dist/server/runtime.d.ts +33 -2
- package/dist/server/session.d.ts +25 -1
- package/dist/server/session.js +18 -4
- package/dist/server/settings.js +142 -10
- package/dist/server/sweeps.d.ts +4 -0
- package/dist/server/sweeps.js +58 -0
- package/dist/server/task-views.d.ts +2 -1
- package/dist/server/task-views.js +96 -6
- package/dist/server/tasks.js +327 -78
- package/dist/server/terminals.d.ts +31 -0
- package/dist/server/terminals.js +365 -0
- package/dist/setup-guide.js +3 -2
- package/dist/slack-chat.js +4 -1
- package/dist/spend-ui.js +3 -3
- package/dist/spend.js +2 -1
- package/dist/steer-capabilities.d.ts +32 -0
- package/dist/steer-capabilities.js +60 -0
- package/dist/steer-controls.d.ts +14 -0
- package/dist/steer-controls.js +36 -0
- package/dist/steer-status.js +2 -2
- package/dist/storage-ui.js +4 -4
- package/dist/store-baseline.d.ts +1 -1
- package/dist/store-baseline.js +1 -1
- package/dist/store.d.ts +440 -15
- package/dist/store.js +1072 -52
- package/dist/subagent-desk.js +1 -1
- package/dist/summary.js +3 -0
- package/dist/task-source.d.ts +58 -0
- package/dist/task-source.js +93 -0
- package/dist/team-leads.js +1 -1
- package/dist/teams-chat.js +4 -1
- package/dist/teardown-git.d.ts +22 -0
- package/dist/teardown-git.js +136 -2
- package/dist/telegram-lead.d.ts +3 -0
- package/dist/telegram-lead.js +44 -7
- package/dist/telegram.d.ts +11 -2
- package/dist/telegram.js +51 -17
- package/dist/terminal-catalog.d.ts +11 -0
- package/dist/terminal-catalog.js +129 -0
- package/dist/text-limits.d.ts +2 -0
- package/dist/text-limits.js +2 -0
- package/dist/tool-broker.d.ts +14 -0
- package/dist/tool-broker.js +67 -2
- package/dist/toolroll-update-ui.js +2 -2
- package/dist/toolroll-update.js +13 -3
- package/dist/tools-ui.js +2 -2
- package/dist/unfenced-test-support.d.ts +2 -0
- package/dist/unfenced-test-support.js +11 -0
- package/dist/webhook-limits.d.ts +23 -0
- package/dist/webhook-limits.js +30 -0
- package/dist/webhook-readers.d.ts +56 -0
- package/dist/webhook-readers.js +138 -0
- package/dist/webhook-signing.d.ts +27 -0
- package/dist/webhook-signing.js +119 -0
- package/dist/work-index.d.ts +29 -0
- package/dist/work-index.js +16 -2
- package/dist/workspace-revision.js +3 -1
- package/dist/writes-mode.d.ts +9 -0
- package/dist/writes-mode.js +8 -0
- package/package.json +15 -2
package/dist/project-memory.js
CHANGED
|
@@ -218,38 +218,63 @@ export function reindexProjectMemory(store, repo) {
|
|
|
218
218
|
insert.run('decision', String(row['id']), repo, 'project', String(row['claim']), String(row['why']));
|
|
219
219
|
});
|
|
220
220
|
}
|
|
221
|
-
/** Index conversation messages incrementally: private threads under their person, team conversations under their id and each project they cover.
|
|
221
|
+
/** Index conversation messages incrementally: private threads under their person, team conversations under their id and each project they cover.
|
|
222
|
+
* A message still waiting for a turn is not indexed: it is indexed when a turn takes it (the admitted-turn pass below,
|
|
223
|
+
* with the words the turn took), so a withdrawn, removed or edited waiting message never reaches search. */
|
|
222
224
|
export function reindexConversations(store) {
|
|
223
|
-
const key = 'memory-search:messages';
|
|
225
|
+
const key = 'memory-search:messages', admittedKey = 'memory-search:admitted';
|
|
224
226
|
const cursor = store.serviceCursor(key);
|
|
225
|
-
const rows = store.handle.prepare('SELECT m.id,m.thread,m.role,m.text,t.approver FROM lead_message m JOIN lead_thread t ON t.id=m.thread WHERE m.id>? ORDER BY m.id LIMIT 500').all(cursor);
|
|
226
|
-
if (rows.length === 0)
|
|
227
|
-
return;
|
|
227
|
+
const rows = store.handle.prepare('SELECT m.id,m.thread,m.role,m.text,m.pending,t.approver FROM lead_message m JOIN lead_thread t ON t.id=m.thread WHERE m.id>? ORDER BY m.id LIMIT 500').all(cursor);
|
|
228
228
|
const insert = store.handle.prepare('INSERT INTO memory_search(kind,ref,repo,scope,title,body) VALUES (?,?,?,?,?,?)');
|
|
229
229
|
const teamOf = store.handle.prepare('SELECT id, projects_json FROM team_conversation WHERE thread=?');
|
|
230
|
+
const indexOne = (row) => {
|
|
231
|
+
const id = Number(row['id']);
|
|
232
|
+
// Indexed exactly as stored (the search proves a hit against the stored words); blank means nothing to index.
|
|
233
|
+
const text = String(row['text']);
|
|
234
|
+
if (!text.trim() || text.length > 20_000)
|
|
235
|
+
return;
|
|
236
|
+
const team = teamOf.get(Number(row['thread']));
|
|
237
|
+
if (team) {
|
|
238
|
+
let projects = [];
|
|
239
|
+
try {
|
|
240
|
+
projects = JSON.parse(String(team['projects_json']));
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
projects = [];
|
|
244
|
+
}
|
|
245
|
+
for (const repo of projects.length ? projects : [''])
|
|
246
|
+
insert.run('conversation', `message:${id}`, repo, `conversation:${String(team['id'])}`, `${row['role']} in a team conversation`, text);
|
|
247
|
+
}
|
|
248
|
+
else
|
|
249
|
+
insert.run('conversation', `message:${id}`, '', `approver:${String(row['approver'])}`, `${row['role']} in a private chat`, text);
|
|
250
|
+
};
|
|
230
251
|
store.transact(() => {
|
|
231
252
|
let last = cursor;
|
|
232
253
|
for (const row of rows) {
|
|
233
254
|
last = Number(row['id']);
|
|
234
|
-
|
|
235
|
-
if (!text || text.length > 20_000)
|
|
255
|
+
if (Number(row['pending']) === 1)
|
|
236
256
|
continue;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
257
|
+
indexOne(row);
|
|
258
|
+
}
|
|
259
|
+
if (rows.length > 0)
|
|
260
|
+
store.setServiceCursor(key, last, new Date());
|
|
261
|
+
// Messages that waited and were then admitted (a personal queue row or a team room message bound to a turn since the
|
|
262
|
+
// last pass): one the walk passed while it waited (at or below the cursor this pass started from) is indexed now,
|
|
263
|
+
// with the words its turn took. One past it is left to the walk, so nothing is indexed twice. Found by the turn
|
|
264
|
+
// indexes on both queues: only queued messages are visited, never every turn. (After an update from before v128, a
|
|
265
|
+
// team message already indexed when it was saved gains a second copy once; search counts each entry once.)
|
|
266
|
+
const turnCursor = store.serviceCursor(admittedKey);
|
|
267
|
+
const admitted = store.handle.prepare(`SELECT message, turn FROM (SELECT message, turn_id AS turn FROM lead_queue WHERE turn_id > ?
|
|
268
|
+
UNION ALL SELECT message, turn_id AS turn FROM team_message WHERE turn_id > ?) ORDER BY turn LIMIT 500`).all(turnCursor, turnCursor);
|
|
269
|
+
if (admitted.length === 0)
|
|
270
|
+
return;
|
|
271
|
+
const taken = store.handle.prepare("SELECT m.id,m.thread,m.role,m.text,t.approver FROM lead_message m JOIN lead_thread t ON t.id=m.thread WHERE m.id=? AND m.pending=0 AND m.id<=?");
|
|
272
|
+
for (const one of admitted) {
|
|
273
|
+
const row = taken.get(Number(one['message']), cursor);
|
|
274
|
+
if (row !== undefined)
|
|
275
|
+
indexOne(row);
|
|
251
276
|
}
|
|
252
|
-
store.setServiceCursor(
|
|
277
|
+
store.setServiceCursor(admittedKey, Number(admitted.at(-1)['turn']), new Date());
|
|
253
278
|
});
|
|
254
279
|
}
|
|
255
280
|
const ftsQuery = (query) => {
|
|
@@ -269,11 +294,18 @@ export function searchMemory(store, args) {
|
|
|
269
294
|
reindexProjectMemory(store, repo);
|
|
270
295
|
const conversations = store.handle.prepare('SELECT conversation FROM team_participant WHERE account=? AND active=1').all(args.actor).map(row => `conversation:${String(row['conversation'])}`);
|
|
271
296
|
const scopes = ['project', `approver:${args.actor}`, ...conversations];
|
|
297
|
+
const limit = Math.min(50, Math.max(1, args.limit ?? 12));
|
|
272
298
|
const match = ftsQuery(query);
|
|
273
299
|
if (!match)
|
|
274
300
|
return [];
|
|
275
301
|
const rows = store.handle.prepare(`SELECT kind, ref, repo, scope, title, snippet(memory_search, 5, '[', ']', '…', 24) AS snippet FROM memory_search
|
|
276
|
-
WHERE memory_search MATCH ? AND scope IN (SELECT value FROM json_each(?)) AND (repo='' OR repo IN (SELECT value FROM json_each(?)))
|
|
277
|
-
|
|
278
|
-
|
|
302
|
+
WHERE memory_search MATCH ? AND scope IN (SELECT value FROM json_each(?)) AND (repo='' OR repo IN (SELECT value FROM json_each(?)))
|
|
303
|
+
AND (kind<>'conversation' OR EXISTS (SELECT 1 FROM lead_message m WHERE m.id=CAST(substr(memory_search.ref, 9) AS INTEGER) AND m.pending=0
|
|
304
|
+
AND (m.text = memory_search.body OR trim(m.text, ' '||char(9)||char(10)||char(13)) = memory_search.body)))
|
|
305
|
+
ORDER BY bm25(memory_search) LIMIT ?`)
|
|
306
|
+
.all(match, JSON.stringify(scopes), JSON.stringify(repos), limit * 2);
|
|
307
|
+
// One hit per entry and place: a message indexed before it waited and again when its turn took it counts once.
|
|
308
|
+
const seen = new Set();
|
|
309
|
+
return rows.filter(row => { const key = `${row['kind']}\u0000${row['ref']}\u0000${row['repo']}\u0000${row['scope']}`; return !seen.has(key) && seen.add(key) !== undefined; }).slice(0, limit)
|
|
310
|
+
.map(row => ({ kind: row['kind'], ref: String(row['ref']), repo: String(row['repo']), title: String(row['title']).slice(0, 200), snippet: String(row['snippet']).slice(0, 400) }));
|
|
279
311
|
}
|
package/dist/project-tools.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Store, ToolActionInfo } from "./store.js";
|
|
2
2
|
import { type BrokerOptions } from "./tool-broker.js";
|
|
3
3
|
import type { FenceMethod } from "./agent-fence.js";
|
|
4
|
+
import type { ProviderId } from "./contracts/provider.js";
|
|
4
5
|
import { type SecretStoreKind } from "./tool-secret-store.js";
|
|
5
6
|
import { type ToolSecret, type ToolSpec } from "./contracts/integration-metadata.js";
|
|
6
7
|
export type { ToolSecret, ToolSpec };
|
|
@@ -169,6 +170,7 @@ export type ToolLaunchArgs = {
|
|
|
169
170
|
cleanup: () => void;
|
|
170
171
|
privateDir?: string;
|
|
171
172
|
serverPids?: () => number[];
|
|
173
|
+
brokerPort?: number;
|
|
172
174
|
};
|
|
173
175
|
/** How a launch's tools are offered: research steps' servers only their read-only actions. */
|
|
174
176
|
export type ToolLaunchOptions = {
|
|
@@ -222,13 +224,13 @@ export type ToolContainment = {
|
|
|
222
224
|
* read (OpenRouter uses the same harness); Claude's own file rules and Linux's bubblewrap don't stop it, and on Linux
|
|
223
225
|
* hidepid hides nothing from the same user, so Linux never starts one.
|
|
224
226
|
*/
|
|
225
|
-
export declare function secretToolRefusal(provider:
|
|
227
|
+
export declare function secretToolRefusal(provider: ProviderId, containment: ToolContainment): string | null;
|
|
226
228
|
/**
|
|
227
229
|
* One attempt's tools, ready for its provider, and recorded on the run:
|
|
228
230
|
* what it launched with and what was left out and why. The caller spawns
|
|
229
231
|
* with `argv`/`env` and calls `cleanup` when the process is done.
|
|
230
232
|
*/
|
|
231
|
-
export declare function prepareRunTools(store: Pick<Store, "projectTools" | "getRun" | "refById" | "getScope" | "toolSealFor" | "recordRunTools" | "orgPolicy">, runId: number, provider:
|
|
233
|
+
export declare function prepareRunTools(store: Pick<Store, "projectTools" | "getRun" | "refById" | "getScope" | "toolSealFor" | "recordRunTools" | "orgPolicy">, runId: number, provider: ProviderId, options: {
|
|
232
234
|
home?: string;
|
|
233
235
|
now: Date;
|
|
234
236
|
containment: ToolContainment;
|
|
@@ -241,7 +243,7 @@ export declare function prepareRunTools(store: Pick<Store, "projectTools" | "get
|
|
|
241
243
|
export declare class RunSignInError extends Error {
|
|
242
244
|
}
|
|
243
245
|
/** When a launch's tools cannot be prepared, it still gets none of anything else's: the same isolation with an empty list. */
|
|
244
|
-
export declare function noToolsArgs(provider:
|
|
246
|
+
export declare function noToolsArgs(provider: ProviderId): ToolLaunchArgs;
|
|
245
247
|
type ToolWrites = Pick<Store, "projectTools" | "addProjectTool" | "removeProjectTool" | "recordProjectToolTest">;
|
|
246
248
|
/** Add one tool (and, for an import, the values it brings) to a project's builds. */
|
|
247
249
|
export declare function addToolTo(store: ToolWrites, repo: string, input: ToolSpec, source: string, by: string, now: Date, options?: {
|
package/dist/project-tools.js
CHANGED
|
@@ -29,6 +29,7 @@ import { fileURLToPath } from "node:url";
|
|
|
29
29
|
import { scanForSecrets } from "./evidence.js";
|
|
30
30
|
import { namedPath } from "./names.js";
|
|
31
31
|
import { startToolBroker } from "./tool-broker.js";
|
|
32
|
+
import { isAcpProvider } from "./acp-agents.js";
|
|
32
33
|
import { SecretStoreError, destroyOwnFile, prepareSecretFolder, registerSecretFolder, pathIn, readOwnFile, readSealed, removeValue, secretBackend, secretFolder, secretStorage, serviceOf, withSecretLock, withSecretLockAsync, writeOwnFile, writeSealed } from "./tool-secret-store.js";
|
|
33
34
|
import { ACCESS, GRANTED, REFRESH, mintRunToken, mintsRunTokens, oneAtATime, runScope, scopeList, signedIn } from "./oauth-run-token.js";
|
|
34
35
|
import { HEADER_NAME, SECRET_NAME, TOOL_NAME as NAME, TOOL_SPEC_LIMITS as LIMITS, readToolSpec } from "./contracts/integration-metadata.js";
|
|
@@ -710,7 +711,7 @@ export async function claudeToolArgs(launch, options = {}) {
|
|
|
710
711
|
omitEnv: [],
|
|
711
712
|
cleanup,
|
|
712
713
|
privateDir: dir,
|
|
713
|
-
...(broker === null ? {} : { serverPids: broker.pids }),
|
|
714
|
+
...(broker === null ? {} : { serverPids: broker.pids, brokerPort: broker.port }),
|
|
714
715
|
};
|
|
715
716
|
}
|
|
716
717
|
/** Where the tool launcher lives beside this module, built. */
|
|
@@ -749,7 +750,7 @@ export async function codexToolArgs(launch, options = {}) {
|
|
|
749
750
|
if (enabled !== undefined)
|
|
750
751
|
argv.push("-c", `${key}.enabled_tools=${tomlList(enabled)}`);
|
|
751
752
|
}
|
|
752
|
-
return { argv, env: {}, omitEnv: [], cleanup, privateDir: dir, serverPids: broker.pids };
|
|
753
|
+
return { argv, env: {}, omitEnv: [], cleanup, privateDir: dir, serverPids: broker.pids, brokerPort: broker.port };
|
|
753
754
|
}
|
|
754
755
|
/** Gemini: no way yet to hand it exactly these servers, so it gets none. */
|
|
755
756
|
export const GEMINI_NO_TOOLS_ARGV = ["--allowed-mcp-server-names", "standing-orders-none"];
|
|
@@ -846,8 +847,9 @@ export function secretToolRefusal(provider, containment) {
|
|
|
846
847
|
export async function prepareRunTools(store, runId, provider, options) {
|
|
847
848
|
const launch = limitedTo(toolLaunchFor(store, runId, options.home), options.readOnly, options.withheld);
|
|
848
849
|
// A local server with secrets starts only where the build can't read its environment; the rest are left out,
|
|
849
|
-
// saying why, before the broker exists. Gemini
|
|
850
|
-
const
|
|
850
|
+
// saying why, before the broker exists. Gemini and ACP agents start none (below).
|
|
851
|
+
const toolless = provider === "gemini" || isAcpProvider(provider);
|
|
852
|
+
const refusal = toolless ? null : secretToolRefusal(provider, options.containment);
|
|
851
853
|
if (refusal !== null) {
|
|
852
854
|
launch.tools = launch.tools.filter(one => {
|
|
853
855
|
if (one.spec.transport === "http" || Object.keys(one.values).length === 0)
|
|
@@ -856,14 +858,15 @@ export async function prepareRunTools(store, runId, provider, options) {
|
|
|
856
858
|
return false;
|
|
857
859
|
});
|
|
858
860
|
}
|
|
859
|
-
const revokes =
|
|
861
|
+
const revokes = toolless ? [] : await signInRun(launch, { readOnly: options.readOnly !== undefined, now: options.now, ...(options.home === undefined ? {} : { home: options.home }), ...(options.fetcher === undefined ? {} : { fetcher: options.fetcher }) });
|
|
860
862
|
const revoke = () => Promise.all(revokes.splice(0).map(one => one())).then(() => undefined);
|
|
861
863
|
const offered = { ...(options.readOnly === undefined ? {} : { enabledTools: options.readOnly }), ...(options.broker === undefined ? {} : { broker: options.broker }) };
|
|
862
864
|
let launched;
|
|
863
865
|
try {
|
|
864
866
|
launched = provider === "claude" ? await claudeToolArgs(launch, offered)
|
|
865
867
|
: provider === "codex" || provider === "openrouter" ? await codexToolArgs(launch, offered)
|
|
866
|
-
: { argv: [...GEMINI_NO_TOOLS_ARGV], env: {}, omitEnv: [], cleanup: () => undefined }
|
|
868
|
+
: provider === "gemini" ? { argv: [...GEMINI_NO_TOOLS_ARGV], env: {}, omitEnv: [], cleanup: () => undefined }
|
|
869
|
+
: { argv: [], env: {}, omitEnv: [], cleanup: () => undefined };
|
|
867
870
|
}
|
|
868
871
|
catch (error) {
|
|
869
872
|
// Tokens were made for a run that won't start: each is revoked, and the run with them.
|
|
@@ -874,13 +877,13 @@ export async function prepareRunTools(store, runId, provider, options) {
|
|
|
874
877
|
}
|
|
875
878
|
// The run's own sign-ins end with it: the broker closes first, so nothing uses a token as it is revoked.
|
|
876
879
|
const prepared = revokes.length === 0 ? launched : { ...launched, cleanup: () => { launched.cleanup(); void revoke(); } };
|
|
877
|
-
const skipped =
|
|
878
|
-
? [...launch.skipped, ...launch.tools.map(one => ({ name: one.spec.name, reason: "Gemini builds can't use project tools yet" }))]
|
|
880
|
+
const skipped = toolless
|
|
881
|
+
? [...launch.skipped, ...launch.tools.map(one => ({ name: one.spec.name, reason: provider === "gemini" ? "Gemini builds can't use project tools yet" : "ACP agents can't use project tools yet" }))]
|
|
879
882
|
: launch.skipped;
|
|
880
883
|
try {
|
|
881
884
|
store.recordRunTools(runId, JSON.stringify({
|
|
882
885
|
provider,
|
|
883
|
-
tools:
|
|
886
|
+
tools: toolless ? [] : launch.tools.map(one => ({ name: one.spec.name, digest: one.digest })),
|
|
884
887
|
skipped,
|
|
885
888
|
}), options.now);
|
|
886
889
|
}
|
package/dist/provider-auth.d.ts
CHANGED
|
@@ -14,16 +14,17 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { Store } from "./store.js";
|
|
16
16
|
import { type ProviderId } from "./provider.js";
|
|
17
|
+
import type { AuthMode } from "./keys.js";
|
|
17
18
|
/** Every sign-in message's dedupe key starts here: `signin:<kind>:<provider>:<ordinal>:<person>`.
|
|
18
19
|
* Not a lifecycle (`life:`) key: those are progress chatter, and an undelivered
|
|
19
20
|
* one stays out of the attention list — this one needs a person. */
|
|
20
21
|
export declare const SIGN_IN_KEY_PREFIX = "signin:";
|
|
21
|
-
export declare const PROVIDER_AUTH_SCHEMA = "\nCREATE TABLE IF NOT EXISTS provider_auth_pause (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n provider TEXT NOT NULL,\n ordinal INTEGER NOT NULL,\n auth_mode TEXT NOT NULL DEFAULT 'subscription' CHECK (auth_mode IN ('subscription', 'api-key')),\n opened_at TEXT NOT NULL,\n first_run INTEGER,\n task_ref INTEGER,\n runs INTEGER NOT NULL DEFAULT 1,\n -- 1 once a sign-in probe during this pause said the provider is signed out:\n -- only a probe that then sees it signed in again may lift the pause.\n probe_saw_out INTEGER NOT NULL DEFAULT 0,\n lifted_at TEXT,\n lifted_by TEXT,\n lifted_how TEXT CHECK (lifted_how IS NULL OR lifted_how IN ('run', 'probe', 'person')),\n resumed INTEGER,\n -- The last task let through as a trial while paused: a sign-in check can\n -- say \"logged in\" for a session that no longer works, so a real run decides.\n last_trial_at TEXT,\n UNIQUE (provider, ordinal)\n);\nCREATE UNIQUE INDEX IF NOT EXISTS provider_auth_pause_open ON provider_auth_pause (provider) WHERE lifted_at IS NULL;\n";
|
|
22
|
+
export declare const PROVIDER_AUTH_SCHEMA = "\nCREATE TABLE IF NOT EXISTS provider_auth_pause (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n provider TEXT NOT NULL,\n ordinal INTEGER NOT NULL,\n auth_mode TEXT NOT NULL DEFAULT 'subscription' CHECK (auth_mode IN ('subscription', 'api-key', 'agent-login')),\n opened_at TEXT NOT NULL,\n first_run INTEGER,\n task_ref INTEGER,\n runs INTEGER NOT NULL DEFAULT 1,\n -- 1 once a sign-in probe during this pause said the provider is signed out:\n -- only a probe that then sees it signed in again may lift the pause.\n probe_saw_out INTEGER NOT NULL DEFAULT 0,\n lifted_at TEXT,\n lifted_by TEXT,\n lifted_how TEXT CHECK (lifted_how IS NULL OR lifted_how IN ('run', 'probe', 'person')),\n resumed INTEGER,\n -- The last task let through as a trial while paused: a sign-in check can\n -- say \"logged in\" for a session that no longer works, so a real run decides.\n last_trial_at TEXT,\n UNIQUE (provider, ordinal)\n);\nCREATE UNIQUE INDEX IF NOT EXISTS provider_auth_pause_open ON provider_auth_pause (provider) WHERE lifted_at IS NULL;\n";
|
|
22
23
|
export type AuthPause = {
|
|
23
24
|
id: number;
|
|
24
25
|
provider: ProviderId;
|
|
25
26
|
ordinal: number;
|
|
26
|
-
authMode:
|
|
27
|
+
authMode: AuthMode;
|
|
27
28
|
openedAt: string;
|
|
28
29
|
firstRun: number | null;
|
|
29
30
|
taskRef: number | null;
|
|
@@ -63,7 +64,7 @@ export declare function openAuthPauses(store: Store): AuthPause[];
|
|
|
63
64
|
*/
|
|
64
65
|
export declare function pauseForAuth(store: Store, args: {
|
|
65
66
|
provider: string;
|
|
66
|
-
authMode:
|
|
67
|
+
authMode: AuthMode | null;
|
|
67
68
|
runId: number;
|
|
68
69
|
taskRef: number;
|
|
69
70
|
now: Date;
|
package/dist/provider-auth.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PROVIDER_IDS } from "./provider.js";
|
|
2
|
+
import { withAcp } from "./acp-agents.js";
|
|
2
3
|
/** Every sign-in message's dedupe key starts here: `signin:<kind>:<provider>:<ordinal>:<person>`.
|
|
3
4
|
* Not a lifecycle (`life:`) key: those are progress chatter, and an undelivered
|
|
4
5
|
* one stays out of the attention list — this one needs a person. */
|
|
@@ -8,7 +9,7 @@ CREATE TABLE IF NOT EXISTS provider_auth_pause (
|
|
|
8
9
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9
10
|
provider TEXT NOT NULL,
|
|
10
11
|
ordinal INTEGER NOT NULL,
|
|
11
|
-
auth_mode TEXT NOT NULL DEFAULT 'subscription' CHECK (auth_mode IN ('subscription', 'api-key')),
|
|
12
|
+
auth_mode TEXT NOT NULL DEFAULT 'subscription' CHECK (auth_mode IN ('subscription', 'api-key', 'agent-login')),
|
|
12
13
|
opened_at TEXT NOT NULL,
|
|
13
14
|
first_run INTEGER,
|
|
14
15
|
task_ref INTEGER,
|
|
@@ -27,8 +28,8 @@ CREATE TABLE IF NOT EXISTS provider_auth_pause (
|
|
|
27
28
|
);
|
|
28
29
|
CREATE UNIQUE INDEX IF NOT EXISTS provider_auth_pause_open ON provider_auth_pause (provider) WHERE lifted_at IS NULL;
|
|
29
30
|
`;
|
|
30
|
-
const NAMES = { claude: "Claude", codex: "Codex", openrouter: "OpenRouter", gemini: "Gemini" };
|
|
31
|
-
const SIGN_IN = { claude: "claude /login", codex: "codex login", gemini: "gemini", openrouter: "toolroll keys set openrouter" };
|
|
31
|
+
const NAMES = withAcp({ claude: "Claude", codex: "Codex", openrouter: "OpenRouter", gemini: "Gemini" }, agent => agent.name);
|
|
32
|
+
const SIGN_IN = withAcp({ claude: "claude /login", codex: "codex login", gemini: "gemini", openrouter: "toolroll keys set openrouter" }, agent => agent.signIn);
|
|
32
33
|
export function providerName(provider) {
|
|
33
34
|
return PROVIDER_IDS.includes(provider) ? NAMES[provider] : provider;
|
|
34
35
|
}
|
|
@@ -57,7 +58,7 @@ function readPause(row) {
|
|
|
57
58
|
id: Number(row["id"]),
|
|
58
59
|
provider: String(row["provider"]),
|
|
59
60
|
ordinal: Number(row["ordinal"]),
|
|
60
|
-
authMode: row["auth_mode"] === "api-key" ? "api-key" : "subscription",
|
|
61
|
+
authMode: row["auth_mode"] === "api-key" ? "api-key" : row["auth_mode"] === "agent-login" ? "agent-login" : "subscription",
|
|
61
62
|
openedAt: String(row["opened_at"]),
|
|
62
63
|
firstRun: row["first_run"] == null ? null : Number(row["first_run"]),
|
|
63
64
|
taskRef: row["task_ref"] == null ? null : Number(row["task_ref"]),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { run, type ExecResult } from "./exec.js";
|
|
2
2
|
import { type ProviderId } from "./provider.js";
|
|
3
|
+
import { type KeyedProviderId } from "./acp-agents.js";
|
|
3
4
|
import { type AuthMode, type KeyVerdict } from "./keys.js";
|
|
4
5
|
export type ProviderConnection = {
|
|
5
6
|
state: "connected" | "signed-out" | "not-installed" | "unverified" | "key-present" | "key-works" | "key-refused" | "missing-key";
|
|
@@ -18,5 +19,5 @@ export declare function createConnectionChecker(options?: {
|
|
|
18
19
|
home?: string;
|
|
19
20
|
clock?: () => Date;
|
|
20
21
|
env?: NodeJS.ProcessEnv;
|
|
21
|
-
verify?: (provider:
|
|
22
|
+
verify?: (provider: KeyedProviderId, key: string) => Promise<KeyVerdict>;
|
|
22
23
|
}): ConnectionChecker;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { run } from "./exec.js";
|
|
4
4
|
import { ALL_CREDENTIAL_ENV, inspectionOf } from "./provider.js";
|
|
5
|
+
import { isAcpProvider } from "./acp-agents.js";
|
|
5
6
|
import { keyStatus, PROVIDER_KEY_ENV, readAuthMode, readProviderKey, verifyProviderKey } from "./keys.js";
|
|
6
7
|
export function signInFacts(provider, result, now = new Date()) {
|
|
7
8
|
const base = { mode: "subscription", checkedAt: now.toISOString() };
|
|
@@ -46,6 +47,10 @@ export function createConnectionChecker(options = {}) {
|
|
|
46
47
|
const cache = new Map();
|
|
47
48
|
const pending = new Map();
|
|
48
49
|
return async (provider, fresh = false) => {
|
|
50
|
+
// An ACP agent is never run to ask how it is signed in (nothing executes it outside the gateway): what is known
|
|
51
|
+
// of it is its conformance check, read elsewhere. Here it is simply unverified.
|
|
52
|
+
if (isAcpProvider(provider))
|
|
53
|
+
return { state: "unverified", mode: "agent-login", checkedAt: clock().toISOString() };
|
|
49
54
|
const mode = readAuthMode(provider, home);
|
|
50
55
|
const keyFacts = keyStatus(provider, home);
|
|
51
56
|
const ambient = !!(options.env ?? process.env)[PROVIDER_KEY_ENV[provider]];
|
package/dist/provider.d.ts
CHANGED
|
@@ -175,6 +175,29 @@ export type ParsedEnvelope = {
|
|
|
175
175
|
* failure terminal seen.
|
|
176
176
|
*/
|
|
177
177
|
structuralTerminal: import("./exhaustion.js").StructuralTerminal | null;
|
|
178
|
+
/** What an ACP agent itself reported it used — kept on the run, never priced. Absent for every other dialect. */
|
|
179
|
+
agentReportedUsage?: string | null;
|
|
180
|
+
/** An ACP conversation's facts the conformance check reads (acp-client.ts AcpEnvelope): nothing else does. */
|
|
181
|
+
acp?: {
|
|
182
|
+
offered: string[];
|
|
183
|
+
exit: {
|
|
184
|
+
how: "eof" | "term" | "kill" | "self" | "error";
|
|
185
|
+
leftover: boolean;
|
|
186
|
+
timedOut: boolean;
|
|
187
|
+
} | null;
|
|
188
|
+
agent: {
|
|
189
|
+
name: string;
|
|
190
|
+
version: string;
|
|
191
|
+
} | null;
|
|
192
|
+
stop: string | null;
|
|
193
|
+
/** The conversation broke the protocol (acpParse's protocolError), or null. */
|
|
194
|
+
protocolError: string | null;
|
|
195
|
+
/** The plane's isolation probes for a check's first step (invoke.ts), when it ran them. */
|
|
196
|
+
isolation?: {
|
|
197
|
+
fileDenied: boolean;
|
|
198
|
+
envClear: boolean;
|
|
199
|
+
};
|
|
200
|
+
};
|
|
178
201
|
};
|
|
179
202
|
type Adapter = {
|
|
180
203
|
binary: string;
|
|
@@ -266,13 +289,13 @@ export declare function adapterFor(provider: ProviderId): Adapter;
|
|
|
266
289
|
*/
|
|
267
290
|
export type ProviderAudit = {
|
|
268
291
|
/** How output reaches us — and therefore which signals can exist at all. */
|
|
269
|
-
transport: "buffered-json" | "streaming-jsonl";
|
|
292
|
+
transport: "buffered-json" | "streaming-jsonl" | "acp-jsonrpc";
|
|
270
293
|
/** Whether a later invocation can resume this provider's session. */
|
|
271
294
|
resume: "native" | "none";
|
|
272
295
|
/** The event whose absence on a failed run means "never initialized".
|
|
273
296
|
* CAPABILITY metadata, not a per-run observation (arc 1 finding 16):
|
|
274
297
|
* whether a given run actually saw it lives in ParsedEnvelope.initObserved. */
|
|
275
|
-
initSignal: "thread.started" | "system-init" | "init-event" | "none";
|
|
298
|
+
initSignal: "thread.started" | "system-init" | "init-event" | "acp-session" | "none";
|
|
276
299
|
/** How session identity is established: "announced" = read back from the
|
|
277
300
|
* harness's own stream; "minted" = the PLANE chooses the id pre-spawn
|
|
278
301
|
* (gemini --session-id) and the envelope must echo it (Phase 3 A5). */
|
package/dist/provider.js
CHANGED
|
@@ -18,13 +18,16 @@
|
|
|
18
18
|
* TOML-quoted deterministically, and the API key excluded from every
|
|
19
19
|
* shell the model itself launches (Codex provider review, Q5).
|
|
20
20
|
*/
|
|
21
|
+
import { INTERPRETER_INJECTION_ENV } from "./acp-attest.js";
|
|
21
22
|
import { claudeFenceSettings, codexFenceArgv } from "./agent-fence.js";
|
|
22
23
|
import { claudeSteerHooks } from "./steer-mailbox.js";
|
|
23
24
|
import {} from "./exec.js";
|
|
24
|
-
import { runStreamJsonl, runClaudeStreamJsonl, runGeminiStreamJsonl, CLAUDE_COST_UNKNOWN } from "./exec.js";
|
|
25
|
+
import { runStreamJsonl, runClaudeStreamJsonl, runGeminiStreamJsonl, runAcpSession, CLAUDE_COST_UNKNOWN } from "./exec.js";
|
|
25
26
|
import { scanForSecrets } from "./evidence.js";
|
|
26
27
|
import { FINDINGS_MODEL_SCHEMA } from "./contracts/review-findings.js";
|
|
27
28
|
import { REVIEW_OUTPUT_LIMITS } from "./structured-output.js";
|
|
29
|
+
import { ACP_AGENTS, isAcpProvider, withAcp } from "./acp-agents.js";
|
|
30
|
+
import { acpParse } from "./acp-client.js";
|
|
28
31
|
import { agentSpecSchema, MODEL_ID, modelIdSchema, PROVIDER_ID_VALUES, providerIdSchema } from "./contracts/provider.js";
|
|
29
32
|
export const PROVIDER_IDS = PROVIDER_ID_VALUES;
|
|
30
33
|
export function isProviderId(value) {
|
|
@@ -787,12 +790,13 @@ export function safeDiagnostic(text) {
|
|
|
787
790
|
* provider gets its OWN keys and nobody else's: S4 (live spike) confirmed
|
|
788
791
|
* what inheritance makes true by construction — an agent's shell reads its
|
|
789
792
|
* process env, so a foreign key in that env is a foreign key disclosed. */
|
|
790
|
-
const CREDENTIAL_ENV = {
|
|
793
|
+
const CREDENTIAL_ENV = withAcp({
|
|
791
794
|
claude: ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"],
|
|
792
795
|
codex: ["OPENAI_API_KEY", "CODEX_API_KEY"],
|
|
793
796
|
openrouter: [OPENROUTER_ENV_KEY],
|
|
794
797
|
gemini: ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
795
|
-
|
|
798
|
+
// An ACP agent signs in its own way; Toolroll holds no key for it, so it sheds every key above.
|
|
799
|
+
}, () => []);
|
|
796
800
|
const foreignCredentialEnv = (own) => Object.entries(CREDENTIAL_ENV)
|
|
797
801
|
.filter(([provider]) => provider !== own)
|
|
798
802
|
.flatMap(([, keys]) => [...keys]);
|
|
@@ -800,7 +804,40 @@ const foreignCredentialEnv = (own) => Object.entries(CREDENTIAL_ENV)
|
|
|
800
804
|
* since a feature check needs no key at all (Codex gemini verify,
|
|
801
805
|
* finding 2). Agent spawns keep their own; probes keep none. */
|
|
802
806
|
export const ALL_CREDENTIAL_ENV = Object.values(CREDENTIAL_ENV).flat();
|
|
803
|
-
|
|
807
|
+
/**
|
|
808
|
+
* An ACP agent's retained record (acp-client.ts) read into the gateway's
|
|
809
|
+
* envelope. Its own usage report is never priced: tokens and cost stay null.
|
|
810
|
+
*/
|
|
811
|
+
function acpEnvelope(stdout) {
|
|
812
|
+
const read = acpParse(stdout);
|
|
813
|
+
return {
|
|
814
|
+
sessionId: read.sessionId,
|
|
815
|
+
protocolError: read.protocolError,
|
|
816
|
+
finalMessage: read.finalMessage,
|
|
817
|
+
tokensIn: null,
|
|
818
|
+
tokensOut: null,
|
|
819
|
+
costUsd: null,
|
|
820
|
+
usageRaw: null,
|
|
821
|
+
initObserved: read.initObserved,
|
|
822
|
+
promptConsumed: read.promptConsumed,
|
|
823
|
+
diagnostic: read.diagnostic === null ? null : safeDiagnostic(read.diagnostic),
|
|
824
|
+
structuralTerminal: read.terminal === null ? null : { failed: true, text: read.terminal.text === null ? null : safeDiagnostic(read.terminal.text), code: read.terminal.code.slice(0, 128) },
|
|
825
|
+
agentReportedUsage: read.reportedUsage,
|
|
826
|
+
acp: { offered: read.offered, exit: read.exit, agent: read.agent, stop: read.stop, protocolError: read.protocolError },
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
function acpAdapter(provider) {
|
|
830
|
+
return {
|
|
831
|
+
binary: ACP_AGENTS[provider].binary,
|
|
832
|
+
argv: () => [...ACP_AGENTS[provider].argv],
|
|
833
|
+
parse: acpEnvelope,
|
|
834
|
+
defaultRunner: runAcpSession,
|
|
835
|
+
// Every key Toolroll knows, and whatever would make the pinned interpreter load code beside the checked script.
|
|
836
|
+
extraOmitEnv: [...foreignCredentialEnv(provider), ...INTERPRETER_INJECTION_ENV],
|
|
837
|
+
clampTimeout: (_phase, requested) => requested,
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
const ADAPTERS = withAcp({
|
|
804
841
|
claude: {
|
|
805
842
|
binary: "claude",
|
|
806
843
|
argv: claudeArgv,
|
|
@@ -852,7 +889,7 @@ const ADAPTERS = {
|
|
|
852
889
|
extraOmitEnv: foreignCredentialEnv("gemini"),
|
|
853
890
|
clampTimeout: (phase, requested) => Math.min(requested, GEMINI_TIMEOUT_CAP_MS[phase]),
|
|
854
891
|
},
|
|
855
|
-
};
|
|
892
|
+
}, (_agent, provider) => acpAdapter(provider));
|
|
856
893
|
/**
|
|
857
894
|
* The spawning surface. Imported by invoke.ts and NOWHERE else — the
|
|
858
895
|
* architecture test reads imports, not string literals, because provider
|
|
@@ -861,7 +898,7 @@ const ADAPTERS = {
|
|
|
861
898
|
export function adapterFor(provider) {
|
|
862
899
|
return ADAPTERS[provider];
|
|
863
900
|
}
|
|
864
|
-
const AUDITS = {
|
|
901
|
+
const AUDITS = withAcp({
|
|
865
902
|
claude: {
|
|
866
903
|
transport: "streaming-jsonl",
|
|
867
904
|
resume: "native",
|
|
@@ -923,8 +960,17 @@ const AUDITS = {
|
|
|
923
960
|
"trusted-folder store (S2, live at 0.57.0: headless REFUSES untrusted directories; the plane grants --skip-trust per-invocation to its own leased worktrees)",
|
|
924
961
|
],
|
|
925
962
|
},
|
|
926
|
-
}
|
|
927
|
-
|
|
963
|
+
}, agent => ({
|
|
964
|
+
// One ACP conversation per build: the prompt's answer is the only success (acp-client.ts), so exit 0 alone is never believed.
|
|
965
|
+
transport: "acp-jsonrpc",
|
|
966
|
+
resume: "none",
|
|
967
|
+
initSignal: "acp-session",
|
|
968
|
+
sessionIdentity: "announced",
|
|
969
|
+
terminalContract: "required",
|
|
970
|
+
isolation: { flag: agent.argv.filter(one => one.startsWith("--") && one !== "--acp").join(" ") || null, resumeSafe: null, enforced: agent.argv.some(one => one.startsWith("--") && one !== "--acp") },
|
|
971
|
+
configSurface: [...agent.configSurface],
|
|
972
|
+
}));
|
|
973
|
+
export const MONEY_CAPABILITIES = withAcp({
|
|
928
974
|
claude: {
|
|
929
975
|
incrementalUsage: true,
|
|
930
976
|
nativeDollarCapFlag: "--max-budget-usd",
|
|
@@ -947,7 +993,12 @@ export const MONEY_CAPABILITIES = {
|
|
|
947
993
|
// numbers cover that invocation only (conformance fixture j).
|
|
948
994
|
usageSemantics: "per-invocation",
|
|
949
995
|
},
|
|
950
|
-
}
|
|
996
|
+
}, () => ({
|
|
997
|
+
// ACP carries no dollars Toolroll trusts: an agent's own usage report is kept apart, unpriced.
|
|
998
|
+
incrementalUsage: false,
|
|
999
|
+
nativeDollarCapFlag: null,
|
|
1000
|
+
usageSemantics: "per-invocation",
|
|
1001
|
+
}));
|
|
951
1002
|
/**
|
|
952
1003
|
* The fail-closed budget-flag probe (finding 24's amendment): resolve
|
|
953
1004
|
* the EXACT executable that will spawn, read its version, and prove the
|
|
@@ -997,6 +1048,9 @@ export function validateSpec(spec) {
|
|
|
997
1048
|
if (spec.provider === "gemini" && spec.model === null) {
|
|
998
1049
|
return { ok: false, problem: "model: gemini needs an explicit model — the harness default drifts with its releases" };
|
|
999
1050
|
}
|
|
1051
|
+
if (isAcpProvider(spec.provider) && spec.model === null) {
|
|
1052
|
+
return { ok: false, problem: `model: ${ACP_AGENTS[spec.provider].name} needs an explicit model — one it offered when it was checked` };
|
|
1053
|
+
}
|
|
1000
1054
|
return { ok: true };
|
|
1001
1055
|
}
|
|
1002
1056
|
/**
|
package/dist/pull-request-ui.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** Settings → Projects → Pull requests: one project's pull-request setup, in plain words. Off shows what setup
|
|
2
2
|
* found (or what to fix) and one Turn on; on shows where pull requests go and how they merge. Every change takes
|
|
3
3
|
* the password; turning on restates the exact repository and branch that were checked. */
|
|
4
|
-
import { html, postForm } from "./html.js";
|
|
4
|
+
import { html, postForm, reauth } from "./html.js";
|
|
5
5
|
const METHOD_WORDS = { squash: "Squash and merge", merge: "Merge commit", rebase: "Rebase and merge" };
|
|
6
6
|
export function pullRequestSettingsHtml(view) {
|
|
7
7
|
const post = (act, body, hidden = {}) => postForm("/settings/pull-requests", body, { attrs: { class: "card pr-settings" }, hidden: { repo: view.repo, act, ...hidden } });
|
|
8
|
-
const password = html `<label>Password<input type="password" name="password" autocomplete="current-password" required></label
|
|
8
|
+
const password = html `${reauth(html `<label>Password<input type="password" name="password" autocomplete="current-password" required></label>`)}`;
|
|
9
9
|
const notes = [
|
|
10
10
|
view.said === null ? "" : html `<p class="notice" role="status">${view.said}</p>`,
|
|
11
11
|
view.problem === null ? "" : html `<p class="problem" role="alert">${view.problem}</p>`,
|
package/dist/push.js
CHANGED
|
@@ -259,7 +259,7 @@ export async function pushPass(store, options) {
|
|
|
259
259
|
// Renew and re-fence immediately before the network (finding 12).
|
|
260
260
|
if (!store.renewPushClaim(pair.id, owner, pair.claimGeneration, PUSH_CLAIM_TTL_MS, clock()))
|
|
261
261
|
continue;
|
|
262
|
-
const fenced = store.pushSendFence(pair.id, owner, pair.claimGeneration);
|
|
262
|
+
const fenced = store.pushSendFence(pair.id, owner, pair.claimGeneration, clock());
|
|
263
263
|
if (fenced === null)
|
|
264
264
|
continue; // resolved, retired, rotated, or lost — nothing to send
|
|
265
265
|
const subscription = fenced.subscriptionRow;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type AttachSeams } from "./console-instance.js";
|
|
2
|
+
export type QuickStartSeams = {
|
|
3
|
+
databaseFile: string;
|
|
4
|
+
cwd: string;
|
|
5
|
+
env: Record<string, string | undefined>;
|
|
6
|
+
build: string;
|
|
7
|
+
/** The repository's top folder for a directory, or null outside one. */
|
|
8
|
+
findRepo: (cwd: string) => Promise<string | null>;
|
|
9
|
+
/** Whether this installation already admitted the repository. */
|
|
10
|
+
enrolled: (repo: string) => Promise<boolean>;
|
|
11
|
+
/** The ordinary admission (`repos add`): its registry lock and ceilings, from this CLI's own database. */
|
|
12
|
+
admit: (repo: string) => Promise<number>;
|
|
13
|
+
/** `up`, for this repository. */
|
|
14
|
+
startUp: () => Promise<number>;
|
|
15
|
+
openBrowser: (url: string) => void | Promise<void>;
|
|
16
|
+
terminal: boolean;
|
|
17
|
+
attach?: AttachSeams;
|
|
18
|
+
/** Tests: the agent check (default: the environment and the process's ancestors). */
|
|
19
|
+
underAgent?: boolean;
|
|
20
|
+
};
|
|
21
|
+
/** Whether argv is a bare `toolroll` (nothing, or only --json). */
|
|
22
|
+
export declare const isBareInvocation: (argv: readonly string[]) => boolean;
|
|
23
|
+
/** null: not this command (outside a repository) — the caller scans as before. */
|
|
24
|
+
export declare function runQuickStart(argv: readonly string[], write: (line: string) => void, seams: QuickStartSeams): Promise<number | null>;
|