agent-tempo 1.7.0-beta.4 → 1.7.0-beta.5
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/dashboard/package.json +1 -1
- package/dist/pi/headless.d.ts +27 -32
- package/dist/pi/headless.js +67 -0
- package/dist/types.d.ts +26 -15
- package/package.json +1 -1
package/dashboard/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-tempo-dashboard",
|
|
3
3
|
"private": true,
|
|
4
|
-
"version": "1.7.0-beta.
|
|
4
|
+
"version": "1.7.0-beta.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Web dashboard for agent-tempo. Bundled into the npm package; served by the daemon at /dashboard/*.",
|
|
7
7
|
"scripts": {
|
package/dist/pi/headless.d.ts
CHANGED
|
@@ -38,42 +38,37 @@ export interface RunHeadlessPiOptions {
|
|
|
38
38
|
continueSessionId?: string;
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
41
|
+
* #715 — compute the registration-level `excludeTools` denylist for
|
|
42
|
+
* `createAgentSession`. Excluded tools are never registered → ABSENT from the
|
|
43
|
+
* model's toolset AND system prompt: the LLM cannot request what it never sees.
|
|
42
44
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* extension can register
|
|
48
|
-
*
|
|
49
|
-
*
|
|
45
|
+
* This is a registration-level FLOOR beneath the call-time MD-C handler + #712
|
|
46
|
+
* gate. It is tamper-RESISTANT, NOT tamper-PROOF: it defends a PROMPT-INJECTED
|
|
47
|
+
* agent (and holds even if the call-time gate had a bug — the tool simply isn't
|
|
48
|
+
* there), but it does NOT defend against PROCESS COMPROMISE — a tampered /
|
|
49
|
+
* modified extension can re-register or un-exclude tools (this is OUR code
|
|
50
|
+
* passing a denylist; an attacker who modifies the code/process bypasses it).
|
|
51
|
+
* That residual is OS-sandbox + supply-chain integrity, tracked as #724.
|
|
50
52
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
53
|
+
* `excludeTools` is matched by NAME against BOTH Pi built-ins AND
|
|
54
|
+
* extension-registered tools (incl. agent-tempo's MCP tools via `renderToPi`), so
|
|
55
|
+
* this list contains ONLY Pi-built-in / exec names — never agent-tempo tool names
|
|
56
|
+
* (`cue`/`report`/`recruit`/…). Posture:
|
|
57
|
+
* - `toolAccess === 'restricted'` → exclude {@link EXEC_TOOLS} (exec/bash
|
|
58
|
+
* registration-absent; a strict upgrade of the prior call-time block, and the
|
|
59
|
+
* headless default → the model never even sees exec).
|
|
60
|
+
* - `guardrailPolicy === 'observe-only'` → also exclude the Pi built-in act
|
|
61
|
+
* tools ({@link PI_BUILTIN_ACT_TOOLS}); read/grep/glob stay. The agent-tempo
|
|
62
|
+
* MCP act tools (recruit/destroy/…) stay covered by the client-side no-act
|
|
63
|
+
* handler (commit 5) — excludeTools handles the Pi built-ins only.
|
|
64
|
+
* - `monitored` / `supervised` / `autonomous` → NO exec exclusion: those tools
|
|
65
|
+
* stay REGISTERED so they can be gated/approved per-use (#712). (`supervised`
|
|
66
|
+
* = approve-and-run, NOT exec-absent.)
|
|
61
67
|
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* which is empty because we pass NO `additionalExtensionPaths`. So
|
|
65
|
-
* `loadExtensions([])` registers nothing from disk/packages.
|
|
66
|
-
* - Inline `extensionFactories` load UNCONDITIONALLY (reload() line 275 is not
|
|
67
|
-
* gated by `noExtensions`), so our agent-tempo extension still attaches.
|
|
68
|
-
* Net: the ONLY tools present are Pi's built-ins (bash/read/edit/write/grep —
|
|
69
|
-
* all covered by the deny-list) + our agent-tempo MCP tools (no exec). No
|
|
70
|
-
* third-party tool can slip past the deny-list. Skills/prompts/themes cannot
|
|
71
|
-
* register tools, so they are not a vector and are left at defaults.
|
|
72
|
-
*
|
|
73
|
-
* Kept as a pure, exported helper so the `noExtensions: true` invariant has a
|
|
74
|
-
* unit regression test (test/pi-headless-loader.test.ts) without needing the Pi
|
|
75
|
-
* SDK installed.
|
|
68
|
+
* Pure + exported so the registration-absence invariant has a unit regression
|
|
69
|
+
* test without the Pi SDK (mirrors {@link buildPiResourceLoaderOptions}).
|
|
76
70
|
*/
|
|
71
|
+
export declare function computeExcludeTools(toolAccess: PiToolAccess, guardrailPolicy: GuardrailPolicy | undefined): string[];
|
|
77
72
|
export declare function buildPiResourceLoaderOptions(params: {
|
|
78
73
|
cwd: string;
|
|
79
74
|
agentDir: string;
|
package/dist/pi/headless.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeExcludeTools = computeExcludeTools;
|
|
3
4
|
exports.buildPiResourceLoaderOptions = buildPiResourceLoaderOptions;
|
|
4
5
|
exports.runHeadlessPi = runHeadlessPi;
|
|
5
6
|
/**
|
|
@@ -31,6 +32,7 @@ exports.runHeadlessPi = runHeadlessPi;
|
|
|
31
32
|
const config_1 = require("../config");
|
|
32
33
|
const sdk_probe_1 = require("../utils/sdk-probe");
|
|
33
34
|
const extension_1 = require("./extension");
|
|
35
|
+
const tool_capability_1 = require("../security/tool-capability");
|
|
34
36
|
const probe_1 = require("./probe");
|
|
35
37
|
const session_seed_1 = require("./session-seed");
|
|
36
38
|
const log = (...args) => {
|
|
@@ -114,6 +116,61 @@ async function resolveModel(modelStr) {
|
|
|
114
116
|
* unit regression test (test/pi-headless-loader.test.ts) without needing the Pi
|
|
115
117
|
* SDK installed.
|
|
116
118
|
*/
|
|
119
|
+
/**
|
|
120
|
+
* Pi BUILT-IN mutating ("act") tool names, excluded at registration for the
|
|
121
|
+
* `observe-only` no-act posture (#715). Pi's default built-ins are
|
|
122
|
+
* read/bash/edit/write; `multiedit` is listed defensively (a no-op if Pi doesn't
|
|
123
|
+
* register it). `bash`/exec are covered by {@link EXEC_TOOLS}. We keep the READ
|
|
124
|
+
* built-ins (read/grep/glob/ls). These are Pi BUILT-IN names — deliberately NOT
|
|
125
|
+
* agent-tempo MCP tool names, so excluding them never removes an agent-tempo
|
|
126
|
+
* coordination tool (those stay handler-gated, commit 5).
|
|
127
|
+
*/
|
|
128
|
+
const PI_BUILTIN_ACT_TOOLS = ['write', 'edit', 'multiedit'];
|
|
129
|
+
/**
|
|
130
|
+
* #715 — compute the registration-level `excludeTools` denylist for
|
|
131
|
+
* `createAgentSession`. Excluded tools are never registered → ABSENT from the
|
|
132
|
+
* model's toolset AND system prompt: the LLM cannot request what it never sees.
|
|
133
|
+
*
|
|
134
|
+
* This is a registration-level FLOOR beneath the call-time MD-C handler + #712
|
|
135
|
+
* gate. It is tamper-RESISTANT, NOT tamper-PROOF: it defends a PROMPT-INJECTED
|
|
136
|
+
* agent (and holds even if the call-time gate had a bug — the tool simply isn't
|
|
137
|
+
* there), but it does NOT defend against PROCESS COMPROMISE — a tampered /
|
|
138
|
+
* modified extension can re-register or un-exclude tools (this is OUR code
|
|
139
|
+
* passing a denylist; an attacker who modifies the code/process bypasses it).
|
|
140
|
+
* That residual is OS-sandbox + supply-chain integrity, tracked as #724.
|
|
141
|
+
*
|
|
142
|
+
* `excludeTools` is matched by NAME against BOTH Pi built-ins AND
|
|
143
|
+
* extension-registered tools (incl. agent-tempo's MCP tools via `renderToPi`), so
|
|
144
|
+
* this list contains ONLY Pi-built-in / exec names — never agent-tempo tool names
|
|
145
|
+
* (`cue`/`report`/`recruit`/…). Posture:
|
|
146
|
+
* - `toolAccess === 'restricted'` → exclude {@link EXEC_TOOLS} (exec/bash
|
|
147
|
+
* registration-absent; a strict upgrade of the prior call-time block, and the
|
|
148
|
+
* headless default → the model never even sees exec).
|
|
149
|
+
* - `guardrailPolicy === 'observe-only'` → also exclude the Pi built-in act
|
|
150
|
+
* tools ({@link PI_BUILTIN_ACT_TOOLS}); read/grep/glob stay. The agent-tempo
|
|
151
|
+
* MCP act tools (recruit/destroy/…) stay covered by the client-side no-act
|
|
152
|
+
* handler (commit 5) — excludeTools handles the Pi built-ins only.
|
|
153
|
+
* - `monitored` / `supervised` / `autonomous` → NO exec exclusion: those tools
|
|
154
|
+
* stay REGISTERED so they can be gated/approved per-use (#712). (`supervised`
|
|
155
|
+
* = approve-and-run, NOT exec-absent.)
|
|
156
|
+
*
|
|
157
|
+
* Pure + exported so the registration-absence invariant has a unit regression
|
|
158
|
+
* test without the Pi SDK (mirrors {@link buildPiResourceLoaderOptions}).
|
|
159
|
+
*/
|
|
160
|
+
function computeExcludeTools(toolAccess, guardrailPolicy) {
|
|
161
|
+
const exclude = new Set();
|
|
162
|
+
if (toolAccess === 'restricted') {
|
|
163
|
+
for (const t of tool_capability_1.EXEC_TOOLS)
|
|
164
|
+
exclude.add(t);
|
|
165
|
+
}
|
|
166
|
+
if (guardrailPolicy === 'observe-only') {
|
|
167
|
+
for (const t of tool_capability_1.EXEC_TOOLS)
|
|
168
|
+
exclude.add(t); // no-act ⊇ no-exec
|
|
169
|
+
for (const t of PI_BUILTIN_ACT_TOOLS)
|
|
170
|
+
exclude.add(t);
|
|
171
|
+
}
|
|
172
|
+
return [...exclude];
|
|
173
|
+
}
|
|
117
174
|
function buildPiResourceLoaderOptions(params) {
|
|
118
175
|
return {
|
|
119
176
|
cwd: params.cwd,
|
|
@@ -192,10 +249,20 @@ async function runHeadlessPi(opts = {}) {
|
|
|
192
249
|
// heartbeat). The SDK's own doc comment (sdk.js:74-83) prescribes this exact
|
|
193
250
|
// construct → reload() → pass-as-resourceLoader sequence.
|
|
194
251
|
await resourceLoader.reload();
|
|
252
|
+
// #715 — registration-level exec/act exclusion (the true "agent physically
|
|
253
|
+
// lacks the tools" boundary; see computeExcludeTools). Excluded tools are never
|
|
254
|
+
// registered, so they're absent from the model's toolset + system prompt — a
|
|
255
|
+
// hard layer beyond the call-time MD-C handler + #712 gate (kept as
|
|
256
|
+
// belt-and-suspenders). Empty for monitored/supervised/autonomous+standard.
|
|
257
|
+
const excludeTools = computeExcludeTools(toolAccess, opts.guardrailPolicy);
|
|
258
|
+
if (excludeTools.length > 0) {
|
|
259
|
+
log(`#715: excluding ${excludeTools.length} tool(s) at registration (toolAccess=${toolAccess}, guardrailPolicy=${opts.guardrailPolicy ?? 'autonomous'}): ${excludeTools.join(', ')}`);
|
|
260
|
+
}
|
|
195
261
|
const { session } = await createAgentSession({
|
|
196
262
|
cwd: process.cwd(),
|
|
197
263
|
agentDir,
|
|
198
264
|
...(model ? { model } : {}),
|
|
265
|
+
...(excludeTools.length > 0 ? { excludeTools } : {}),
|
|
199
266
|
resourceLoader,
|
|
200
267
|
// H1 (#645): in-memory session (seeded above via the session-seed chokepoint).
|
|
201
268
|
// H2 will seed it from agent-tempo durable state (ENV.PI_CONTINUE_SESSION
|
package/dist/types.d.ts
CHANGED
|
@@ -238,22 +238,33 @@ export interface SessionMetadata {
|
|
|
238
238
|
* the real posture on EVERY attach (across restart / migrate / re-attach), so
|
|
239
239
|
* a previously-`supervised` agent stays supervised. (tempo-architect ruling.)
|
|
240
240
|
*
|
|
241
|
-
* **★ Enforcement scope (#715).** `supervised` is the daemon-enforced
|
|
242
|
-
* boundary for the realistic threat: a prompt-injected agent. A
|
|
243
|
-
* can only *emit* tool-call requests — Pi routes every one to
|
|
244
|
-
* `tool_call` handler, which engages the gate (non-`low-risk
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
* `closed` on any lookup failure — no-fail-open), so an engaging agent can't
|
|
249
|
-
* self-downgrade a supervised player out of fail-closed.
|
|
241
|
+
* **★ Enforcement scope (#712/#715).** `supervised` is the daemon-enforced
|
|
242
|
+
* approval boundary for the realistic threat: a prompt-injected agent. A
|
|
243
|
+
* manipulated LLM can only *emit* tool-call requests — Pi routes every one to
|
|
244
|
+
* agent-tempo's `tool_call` handler, which engages the gate (non-`low-risk`;
|
|
245
|
+
* #712 daemon-computes `failMode` from this durable policy, falling `closed` on
|
|
246
|
+
* any lookup failure — no-fail-open, so an engaging agent can't self-downgrade).
|
|
247
|
+
* The agent **cannot** skip the gate — it doesn't control the hook.
|
|
250
248
|
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
249
|
+
* **#715 adds a registration-level floor.** For `toolAccess: 'restricted'` (and
|
|
250
|
+
* `observe-only`'s act tools) the exec/act tools are EXCLUDED at
|
|
251
|
+
* `createAgentSession` (`excludeTools`) → **absent** from the model's toolset and
|
|
252
|
+
* system prompt entirely; the LLM cannot request what it never sees. That is
|
|
253
|
+
* stronger than a call-time block — it holds even if the call-time gate had a bug
|
|
254
|
+
* (the tool simply isn't there). `supervised` with exec present keeps exec
|
|
255
|
+
* **present + gated** (approve-per-use), so this floor applies to the exec/no-act
|
|
256
|
+
* postures, not to a `supervised`+`standard` player.
|
|
257
|
+
*
|
|
258
|
+
* **Residual (all postures): process compromise** — code execution *inside* the
|
|
259
|
+
* Pi process (in-process syscalls; host RCE bypassing the handler), OR a
|
|
260
|
+
* tampered / modified extension that un-excludes or re-registers tools.
|
|
261
|
+
* `excludeTools` is OUR code passing a denylist; an attacker who modifies that
|
|
262
|
+
* code or the process bypasses it. The only defense is OS-level sandboxing +
|
|
263
|
+
* supply-chain integrity, a separate future `'sandboxed'` posture (#724). So:
|
|
264
|
+
* **tamper-RESISTANT** vs prompt-injection + an honest gate bug; **NOT
|
|
265
|
+
* tamper-PROOF** vs a compromised process. Against prompt-injection — the
|
|
266
|
+
* realistic threat — it **is** a real enforcement boundary; #724 is not a gap in
|
|
267
|
+
* that scope.
|
|
257
268
|
*
|
|
258
269
|
* **Post-restart window:** on daemon restart the in-memory ingest tokens are
|
|
259
270
|
* invalidated, so existing players' gate engagements are rejected (403) until a
|