@bridge4dev/runner 0.60.0 → 0.62.0
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/README.md +12 -6
- package/dist/adapters/claude.js +83 -19
- package/dist/adapters/codex.js +102 -17
- package/dist/adapters/questions.js +1 -0
- package/dist/adapters/types.d.ts +92 -1
- package/dist/adapters/types.js +59 -0
- package/dist/index.js +12 -0
- package/dist/journal.d.ts +40 -0
- package/dist/journal.js +82 -0
- package/dist/policy.d.ts +27 -0
- package/dist/policy.js +38 -5
- package/dist/protocol.d.ts +107 -0
- package/dist/protocol.js +29 -0
- package/dist/supervisor.d.ts +87 -2
- package/dist/supervisor.js +507 -89
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/journal.js
CHANGED
|
@@ -21,6 +21,23 @@ export class SessionJournal {
|
|
|
21
21
|
lastStatus = null;
|
|
22
22
|
/** The agent's conversation tip, and the provider session it lives in. */
|
|
23
23
|
lastAnchor = null;
|
|
24
|
+
/**
|
|
25
|
+
* Every card this journal has published, and whether it is still open —
|
|
26
|
+
* in the order they were asked (#392).
|
|
27
|
+
*
|
|
28
|
+
* The persistent twin of the supervisor's `running.openQuestions`: that set
|
|
29
|
+
* dies with the process, and a process that dies ungracefully never gets to
|
|
30
|
+
* `shutdown()`, which is the only place it withdraws them. This one is
|
|
31
|
+
* rebuilt from the file, so the next process can close what the last one
|
|
32
|
+
* left open — the card in the browser does not know the runner restarted.
|
|
33
|
+
*
|
|
34
|
+
* Closed cards are remembered too, not only dropped: «this card was closed
|
|
35
|
+
* by somebody» is the fact that stops a SECOND tombstone. A queued answer
|
|
36
|
+
* that reaches the next process after a restore has already closed the card
|
|
37
|
+
* must rescue the words and say nothing more about the card — and the only
|
|
38
|
+
* witness that the card was closed is this file.
|
|
39
|
+
*/
|
|
40
|
+
asks = new Map();
|
|
24
41
|
constructor(sessionId, dir = journalDir()) {
|
|
25
42
|
this.sessionId = sessionId;
|
|
26
43
|
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
@@ -50,6 +67,16 @@ export class SessionJournal {
|
|
|
50
67
|
});
|
|
51
68
|
if (parsed.seq >= this.nextSeq)
|
|
52
69
|
this.nextSeq = parsed.seq + 1;
|
|
70
|
+
// The event lines carry the same fact as `ask_open`/`ask_closed`, and
|
|
71
|
+
// replaying both is harmless: the set is a set.
|
|
72
|
+
this.trackAsk(parsed.eventType, parsed.payload, false);
|
|
73
|
+
}
|
|
74
|
+
else if (parsed.kind === 'ask_open') {
|
|
75
|
+
if (this.asks.get(parsed.askId) !== 'closed')
|
|
76
|
+
this.asks.set(parsed.askId, 'open');
|
|
77
|
+
}
|
|
78
|
+
else if (parsed.kind === 'ask_closed') {
|
|
79
|
+
this.asks.set(parsed.askId, 'closed');
|
|
53
80
|
}
|
|
54
81
|
else if (parsed.kind === 'ack') {
|
|
55
82
|
this.unackedBySeq.delete(parsed.seq);
|
|
@@ -107,6 +134,11 @@ export class SessionJournal {
|
|
|
107
134
|
if (this.lastAnchor) {
|
|
108
135
|
lines.push({ kind: 'anchor', ...this.lastAnchor });
|
|
109
136
|
}
|
|
137
|
+
// AFTER the event lines: an unacked `question` re-written above would
|
|
138
|
+
// otherwise reopen, on the next replay, a card these lines say is closed.
|
|
139
|
+
for (const [askId, state] of this.asks) {
|
|
140
|
+
lines.push({ kind: state === 'open' ? 'ask_open' : 'ask_closed', askId });
|
|
141
|
+
}
|
|
110
142
|
if (this.lastStatus) {
|
|
111
143
|
lines.push({
|
|
112
144
|
kind: 'status',
|
|
@@ -182,8 +214,58 @@ export class SessionJournal {
|
|
|
182
214
|
const event = { seq: this.nextSeq++, eventType, payload };
|
|
183
215
|
this.write({ kind: 'event', ...event, ts: new Date().toISOString() });
|
|
184
216
|
this.unackedBySeq.set(event.seq, event);
|
|
217
|
+
this.trackAsk(eventType, payload, true);
|
|
185
218
|
return event;
|
|
186
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* Keep `openAsks` in step with the cards going out through `append` (#392).
|
|
222
|
+
*
|
|
223
|
+
* Here and not in the supervisor, because `append` is the one door every
|
|
224
|
+
* event takes: a card that was published was published through it, and so
|
|
225
|
+
* was every resolution — the adapter's own, `withdrawOpenQuestions`, the
|
|
226
|
+
* `not_open` miss. A set kept beside the callers would need every one of
|
|
227
|
+
* them to remember it, which is exactly how the in-memory set came to be
|
|
228
|
+
* empty at the moment it was needed.
|
|
229
|
+
*
|
|
230
|
+
* A resolution for a card this journal never published writes nothing:
|
|
231
|
+
* there is nothing to close, and remembering strangers would only grow the
|
|
232
|
+
* file.
|
|
233
|
+
*/
|
|
234
|
+
trackAsk(eventType, payload, persist) {
|
|
235
|
+
if (eventType !== 'question' && eventType !== 'question_resolved')
|
|
236
|
+
return;
|
|
237
|
+
const askId = payload['askId'];
|
|
238
|
+
if (typeof askId !== 'string' || !askId)
|
|
239
|
+
return;
|
|
240
|
+
const state = this.asks.get(askId);
|
|
241
|
+
if (eventType === 'question') {
|
|
242
|
+
// A card is asked once; a replayed event line for one already closed
|
|
243
|
+
// (compaction keeps unacked events) must not reopen it.
|
|
244
|
+
if (state !== undefined)
|
|
245
|
+
return;
|
|
246
|
+
this.asks.set(askId, 'open');
|
|
247
|
+
if (persist)
|
|
248
|
+
this.write({ kind: 'ask_open', askId });
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (state !== 'open')
|
|
252
|
+
return;
|
|
253
|
+
this.asks.set(askId, 'closed');
|
|
254
|
+
if (persist)
|
|
255
|
+
this.write({ kind: 'ask_closed', askId });
|
|
256
|
+
}
|
|
257
|
+
/** The cards still waiting on a person, oldest first (#392). */
|
|
258
|
+
openAskIds() {
|
|
259
|
+
return [...this.asks].filter(([, state]) => state === 'open').map(([askId]) => askId);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* What this journal knows about one card (#392): `open`, `closed`, or
|
|
263
|
+
* nothing at all — a card asked by a runner from before these lines existed,
|
|
264
|
+
* or one this state directory never saw.
|
|
265
|
+
*/
|
|
266
|
+
askState(askId) {
|
|
267
|
+
return this.asks.get(askId);
|
|
268
|
+
}
|
|
187
269
|
/**
|
|
188
270
|
* Never reuse a seq the API already stored: after a runner state-dir wipe the
|
|
189
271
|
* local counter restarts at 1 and every replayed event would collide with an
|
package/dist/policy.d.ts
CHANGED
|
@@ -40,6 +40,33 @@ export interface AgentGitPolicy {
|
|
|
40
40
|
agentAllowForcePush?: boolean;
|
|
41
41
|
/** `git reset --hard` and `git clean`. `undefined` and `false` mean no. */
|
|
42
42
|
agentAllowDestructiveGit?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* #418: may the agent read and write OUTSIDE the folder this session works
|
|
45
|
+
* in — other projects on the same machine included?
|
|
46
|
+
*
|
|
47
|
+
* READ AS `=== true`. `undefined` means an API too old to send the field, or
|
|
48
|
+
* a value the protocol schema threw away, and there the answer must be what
|
|
49
|
+
* it has always been: confined to the folder. The same direction as
|
|
50
|
+
* `agentAllowForcePush` two fields up, the opposite of `agentPushBan` at the
|
|
51
|
+
* top — one names a permission, the other names a ban, and silence refuses
|
|
52
|
+
* in both readings (gotcha 193).
|
|
53
|
+
*
|
|
54
|
+
* **Not a git setting, and it lives in a git-named object on purpose.** The
|
|
55
|
+
* whole delivery pipe for the project's per-agent policy already exists —
|
|
56
|
+
* descriptor → `gitPolicyOf` → `SessionSpec` → `policyContextFor` →
|
|
57
|
+
* `PolicyContext`, with `workspace_settings` for a live change — and it moves
|
|
58
|
+
* this object WHOLE at every hop. A field of its own beside it would have
|
|
59
|
+
* been a second branch in the supervisor, a second one in the live frame and
|
|
60
|
+
* a second chance to forget one of them (plan R8). The name is the price;
|
|
61
|
+
* this comment is the receipt.
|
|
62
|
+
*
|
|
63
|
+
* What it does NOT lift: `isSecretPath`, `isGitInternalPath`,
|
|
64
|
+
* `ctx.agentPromptFile`, `DENIED_COMMAND_PATTERNS`, `SECRET_COMMAND_PATTERNS`,
|
|
65
|
+
* the git policy above, the auto-commit refusal and the heap ceiling. It
|
|
66
|
+
* lifts exactly one rule — «this path is not inside the folder» — and leaves
|
|
67
|
+
* every other reason to refuse standing.
|
|
68
|
+
*/
|
|
69
|
+
agentAllowOutsideFolder?: boolean;
|
|
43
70
|
}
|
|
44
71
|
export interface PolicyContext extends AgentGitPolicy {
|
|
45
72
|
trustMode: TrustMode;
|
package/dist/policy.js
CHANGED
|
@@ -11,6 +11,8 @@ function resolveGitPolicy(ctx) {
|
|
|
11
11
|
protectedBranches: ctx.agentProtectedBranches ?? DEFAULT_PROTECTED_BRANCHES,
|
|
12
12
|
allowForcePush: ctx.agentAllowForcePush === true,
|
|
13
13
|
allowDestructiveGit: ctx.agentAllowDestructiveGit === true,
|
|
14
|
+
// `=== true`: silence keeps the agent in its folder (#418).
|
|
15
|
+
allowOutsideFolder: ctx.agentAllowOutsideFolder === true,
|
|
14
16
|
};
|
|
15
17
|
}
|
|
16
18
|
// ─── Secret masking (plan §8.7) ──────────────────────────────────────
|
|
@@ -1309,6 +1311,18 @@ export function evaluateToolUse(toolName, input, ctx) {
|
|
|
1309
1311
|
if (READ_TOOLS.has(toolName) || WRITE_TOOLS.has(toolName)) {
|
|
1310
1312
|
const rawPath = String(input['file_path'] ?? input['path'] ?? input['notebook_path'] ?? '');
|
|
1311
1313
|
const resolved = rawPath ? normalize(rawPath, ctx.worktreePath) : ctx.worktreePath;
|
|
1314
|
+
/**
|
|
1315
|
+
* #418: this project answered «yes» to «may the agent work outside the
|
|
1316
|
+
* project folder», so «not inside the folder» stops being a reason on its
|
|
1317
|
+
* own. Resolved through `resolveGitPolicy` rather than read off `ctx` here,
|
|
1318
|
+
* because that function is the one place in this file where an absent field
|
|
1319
|
+
* is given its safe meaning — and «unknown» here has to mean «stay in».
|
|
1320
|
+
*
|
|
1321
|
+
* It moves exactly one rule out of the way. Everything checked above and
|
|
1322
|
+
* below this line — secret paths, `.git` internals, the session's own
|
|
1323
|
+
* prompt file — is checked in the same order and refuses the same things.
|
|
1324
|
+
*/
|
|
1325
|
+
const outsideAllowed = resolveGitPolicy(ctx).allowOutsideFolder;
|
|
1312
1326
|
if (isSecretPath(resolved)) {
|
|
1313
1327
|
return { decision: 'deny', reason: 'protected secret path' };
|
|
1314
1328
|
}
|
|
@@ -1328,7 +1342,9 @@ export function evaluateToolUse(toolName, input, ctx) {
|
|
|
1328
1342
|
reason: 'writing inside .git is not allowed — a hook or a config entry is code git runs on its own, past every rule here',
|
|
1329
1343
|
};
|
|
1330
1344
|
}
|
|
1331
|
-
if (WRITE_TOOLS.has(toolName) &&
|
|
1345
|
+
if (WRITE_TOOLS.has(toolName) &&
|
|
1346
|
+
!outsideAllowed &&
|
|
1347
|
+
!isInsideWorktree(resolved, ctx.worktreePath)) {
|
|
1332
1348
|
return { decision: 'deny', reason: 'writes outside the session worktree are not allowed' };
|
|
1333
1349
|
}
|
|
1334
1350
|
// The project's own prompt file — the same rule as `.git` above, for the
|
|
@@ -1344,10 +1360,27 @@ export function evaluateToolUse(toolName, input, ctx) {
|
|
|
1344
1360
|
}
|
|
1345
1361
|
if (trust === 'STRICT')
|
|
1346
1362
|
return { decision: 'ask', reason: 'strict mode' };
|
|
1347
|
-
if (
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1363
|
+
if (!isInsideWorktree(resolved, ctx.worktreePath)) {
|
|
1364
|
+
/**
|
|
1365
|
+
* #418. Below STRICT — which asked one line up and goes on asking, which
|
|
1366
|
+
* is the whole of «Strict still asks» — a path outside the folder is now
|
|
1367
|
+
* an ORDINARY path for a project that allowed it: no card, no refusal,
|
|
1368
|
+
* and the same answer for a read and for a write.
|
|
1369
|
+
*
|
|
1370
|
+
* Its own reason string rather than falling through to «inside worktree»
|
|
1371
|
+
* below: that sentence goes to the log, and about a file in another
|
|
1372
|
+
* folder it would simply be false.
|
|
1373
|
+
*/
|
|
1374
|
+
if (outsideAllowed) {
|
|
1375
|
+
return { decision: 'allow', reason: 'outside the project folder, allowed by this project' };
|
|
1376
|
+
}
|
|
1377
|
+
// Reads only: a write outside was refused above unless the project
|
|
1378
|
+
// allowed it, so nothing else reaches this line.
|
|
1379
|
+
if (READ_TOOLS.has(toolName)) {
|
|
1380
|
+
return trust === 'AUTO'
|
|
1381
|
+
? { decision: 'allow', reason: 'auto mode' }
|
|
1382
|
+
: { decision: 'ask', reason: 'read outside the worktree' };
|
|
1383
|
+
}
|
|
1351
1384
|
}
|
|
1352
1385
|
return { decision: 'allow', reason: 'inside worktree' };
|
|
1353
1386
|
}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -32,6 +32,20 @@ export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
|
32
32
|
* behaviour every runner had before this release.
|
|
33
33
|
*/
|
|
34
34
|
pausedUntil: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
35
|
+
/**
|
|
36
|
+
* Subagents the API still believes to be alive in this session (#393).
|
|
37
|
+
*
|
|
38
|
+
* The number is the dead process's last word, kept by the API: a runner
|
|
39
|
+
* restart takes every subagent with it, and the new process seeds its own
|
|
40
|
+
* count at zero — so the descriptor is the only place the number survives
|
|
41
|
+
* long enough to be written down. The API sends it only while it still
|
|
42
|
+
* believes it (its own trust window on the count's age); absent or zero
|
|
43
|
+
* means «nothing to say», and a restore says nothing.
|
|
44
|
+
*
|
|
45
|
+
* `.catch(undefined)` like its neighbours: one malformed value must cost
|
|
46
|
+
* its own session a line at most, never the frame (QA-100 MAJOR-1).
|
|
47
|
+
*/
|
|
48
|
+
backgroundTasks: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
35
49
|
/**
|
|
36
50
|
* The newest published version of THIS session's agent — Р13, §4.8.
|
|
37
51
|
*
|
|
@@ -99,6 +113,18 @@ export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
|
99
113
|
agentProtectedBranches: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
100
114
|
agentAllowForcePush: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
101
115
|
agentAllowDestructiveGit: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
116
|
+
/**
|
|
117
|
+
* #418: may the agent read and write OUTSIDE the project folder?
|
|
118
|
+
*
|
|
119
|
+
* `.optional().catch(undefined)` for the QA-100 MAJOR-1 reason above, and
|
|
120
|
+
* safe here for the same reason as `agentPushBan` — though the polarity
|
|
121
|
+
* runs the other way. `policy.ts` resolves this one with `=== true`, so a
|
|
122
|
+
* value this schema throws away leaves the session confined to its folder:
|
|
123
|
+
* strictly more restricted, never less. Rubbish in this field therefore
|
|
124
|
+
* costs a project its permission, which is the direction a dropped value is
|
|
125
|
+
* allowed to fail in.
|
|
126
|
+
*/
|
|
127
|
+
agentAllowOutsideFolder: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
102
128
|
budgetUsd: z.ZodNullable<z.ZodNumber>;
|
|
103
129
|
budgetMinutes: z.ZodNullable<z.ZodNumber>;
|
|
104
130
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -114,6 +140,7 @@ export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
|
114
140
|
agentProtectedBranches?: string[] | undefined;
|
|
115
141
|
agentAllowForcePush?: boolean | undefined;
|
|
116
142
|
agentAllowDestructiveGit?: boolean | undefined;
|
|
143
|
+
agentAllowOutsideFolder?: boolean | undefined;
|
|
117
144
|
}, {
|
|
118
145
|
path: string;
|
|
119
146
|
id: string;
|
|
@@ -127,6 +154,7 @@ export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
|
127
154
|
agentProtectedBranches?: unknown;
|
|
128
155
|
agentAllowForcePush?: unknown;
|
|
129
156
|
agentAllowDestructiveGit?: unknown;
|
|
157
|
+
agentAllowOutsideFolder?: unknown;
|
|
130
158
|
}>;
|
|
131
159
|
/**
|
|
132
160
|
* «Run this one without the project's agent prompt.» Absent means no — both
|
|
@@ -234,6 +262,7 @@ export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
|
234
262
|
agentProtectedBranches?: string[] | undefined;
|
|
235
263
|
agentAllowForcePush?: boolean | undefined;
|
|
236
264
|
agentAllowDestructiveGit?: boolean | undefined;
|
|
265
|
+
agentAllowOutsideFolder?: boolean | undefined;
|
|
237
266
|
};
|
|
238
267
|
tickets: {
|
|
239
268
|
number: number;
|
|
@@ -246,6 +275,7 @@ export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
|
246
275
|
} | undefined;
|
|
247
276
|
workMode?: "DIRECT" | "BRANCH" | undefined;
|
|
248
277
|
pausedUntil?: string | null | undefined;
|
|
278
|
+
backgroundTasks?: number | undefined;
|
|
249
279
|
agentLatestVersion?: string | undefined;
|
|
250
280
|
skipAgentPrompt?: boolean | undefined;
|
|
251
281
|
branchHint?: string | undefined;
|
|
@@ -275,6 +305,7 @@ export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
|
275
305
|
agentProtectedBranches?: unknown;
|
|
276
306
|
agentAllowForcePush?: unknown;
|
|
277
307
|
agentAllowDestructiveGit?: unknown;
|
|
308
|
+
agentAllowOutsideFolder?: unknown;
|
|
278
309
|
};
|
|
279
310
|
tickets: {
|
|
280
311
|
number: number;
|
|
@@ -295,6 +326,7 @@ export declare const SessionDescriptorSchema: z.ZodObject<{
|
|
|
295
326
|
activeMsBase?: number | undefined;
|
|
296
327
|
extraBudgetMinutes?: number | null | undefined;
|
|
297
328
|
pausedUntil?: unknown;
|
|
329
|
+
backgroundTasks?: unknown;
|
|
298
330
|
agentLatestVersion?: unknown;
|
|
299
331
|
skipAgentPrompt?: unknown;
|
|
300
332
|
branchHint?: unknown;
|
|
@@ -450,6 +482,20 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
450
482
|
* behaviour every runner had before this release.
|
|
451
483
|
*/
|
|
452
484
|
pausedUntil: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
485
|
+
/**
|
|
486
|
+
* Subagents the API still believes to be alive in this session (#393).
|
|
487
|
+
*
|
|
488
|
+
* The number is the dead process's last word, kept by the API: a runner
|
|
489
|
+
* restart takes every subagent with it, and the new process seeds its own
|
|
490
|
+
* count at zero — so the descriptor is the only place the number survives
|
|
491
|
+
* long enough to be written down. The API sends it only while it still
|
|
492
|
+
* believes it (its own trust window on the count's age); absent or zero
|
|
493
|
+
* means «nothing to say», and a restore says nothing.
|
|
494
|
+
*
|
|
495
|
+
* `.catch(undefined)` like its neighbours: one malformed value must cost
|
|
496
|
+
* its own session a line at most, never the frame (QA-100 MAJOR-1).
|
|
497
|
+
*/
|
|
498
|
+
backgroundTasks: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
453
499
|
/**
|
|
454
500
|
* The newest published version of THIS session's agent — Р13, §4.8.
|
|
455
501
|
*
|
|
@@ -517,6 +563,18 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
517
563
|
agentProtectedBranches: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
518
564
|
agentAllowForcePush: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
519
565
|
agentAllowDestructiveGit: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
566
|
+
/**
|
|
567
|
+
* #418: may the agent read and write OUTSIDE the project folder?
|
|
568
|
+
*
|
|
569
|
+
* `.optional().catch(undefined)` for the QA-100 MAJOR-1 reason above, and
|
|
570
|
+
* safe here for the same reason as `agentPushBan` — though the polarity
|
|
571
|
+
* runs the other way. `policy.ts` resolves this one with `=== true`, so a
|
|
572
|
+
* value this schema throws away leaves the session confined to its folder:
|
|
573
|
+
* strictly more restricted, never less. Rubbish in this field therefore
|
|
574
|
+
* costs a project its permission, which is the direction a dropped value is
|
|
575
|
+
* allowed to fail in.
|
|
576
|
+
*/
|
|
577
|
+
agentAllowOutsideFolder: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
520
578
|
budgetUsd: z.ZodNullable<z.ZodNumber>;
|
|
521
579
|
budgetMinutes: z.ZodNullable<z.ZodNumber>;
|
|
522
580
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -532,6 +590,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
532
590
|
agentProtectedBranches?: string[] | undefined;
|
|
533
591
|
agentAllowForcePush?: boolean | undefined;
|
|
534
592
|
agentAllowDestructiveGit?: boolean | undefined;
|
|
593
|
+
agentAllowOutsideFolder?: boolean | undefined;
|
|
535
594
|
}, {
|
|
536
595
|
path: string;
|
|
537
596
|
id: string;
|
|
@@ -545,6 +604,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
545
604
|
agentProtectedBranches?: unknown;
|
|
546
605
|
agentAllowForcePush?: unknown;
|
|
547
606
|
agentAllowDestructiveGit?: unknown;
|
|
607
|
+
agentAllowOutsideFolder?: unknown;
|
|
548
608
|
}>;
|
|
549
609
|
/**
|
|
550
610
|
* «Run this one without the project's agent prompt.» Absent means no — both
|
|
@@ -652,6 +712,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
652
712
|
agentProtectedBranches?: string[] | undefined;
|
|
653
713
|
agentAllowForcePush?: boolean | undefined;
|
|
654
714
|
agentAllowDestructiveGit?: boolean | undefined;
|
|
715
|
+
agentAllowOutsideFolder?: boolean | undefined;
|
|
655
716
|
};
|
|
656
717
|
tickets: {
|
|
657
718
|
number: number;
|
|
@@ -664,6 +725,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
664
725
|
} | undefined;
|
|
665
726
|
workMode?: "DIRECT" | "BRANCH" | undefined;
|
|
666
727
|
pausedUntil?: string | null | undefined;
|
|
728
|
+
backgroundTasks?: number | undefined;
|
|
667
729
|
agentLatestVersion?: string | undefined;
|
|
668
730
|
skipAgentPrompt?: boolean | undefined;
|
|
669
731
|
branchHint?: string | undefined;
|
|
@@ -693,6 +755,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
693
755
|
agentProtectedBranches?: unknown;
|
|
694
756
|
agentAllowForcePush?: unknown;
|
|
695
757
|
agentAllowDestructiveGit?: unknown;
|
|
758
|
+
agentAllowOutsideFolder?: unknown;
|
|
696
759
|
};
|
|
697
760
|
tickets: {
|
|
698
761
|
number: number;
|
|
@@ -713,6 +776,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
713
776
|
activeMsBase?: number | undefined;
|
|
714
777
|
extraBudgetMinutes?: number | null | undefined;
|
|
715
778
|
pausedUntil?: unknown;
|
|
779
|
+
backgroundTasks?: unknown;
|
|
716
780
|
agentLatestVersion?: unknown;
|
|
717
781
|
skipAgentPrompt?: unknown;
|
|
718
782
|
branchHint?: unknown;
|
|
@@ -747,6 +811,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
747
811
|
agentProtectedBranches?: string[] | undefined;
|
|
748
812
|
agentAllowForcePush?: boolean | undefined;
|
|
749
813
|
agentAllowDestructiveGit?: boolean | undefined;
|
|
814
|
+
agentAllowOutsideFolder?: boolean | undefined;
|
|
750
815
|
};
|
|
751
816
|
tickets: {
|
|
752
817
|
number: number;
|
|
@@ -759,6 +824,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
759
824
|
} | undefined;
|
|
760
825
|
workMode?: "DIRECT" | "BRANCH" | undefined;
|
|
761
826
|
pausedUntil?: string | null | undefined;
|
|
827
|
+
backgroundTasks?: number | undefined;
|
|
762
828
|
agentLatestVersion?: string | undefined;
|
|
763
829
|
skipAgentPrompt?: boolean | undefined;
|
|
764
830
|
branchHint?: string | undefined;
|
|
@@ -794,6 +860,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
794
860
|
agentProtectedBranches?: unknown;
|
|
795
861
|
agentAllowForcePush?: unknown;
|
|
796
862
|
agentAllowDestructiveGit?: unknown;
|
|
863
|
+
agentAllowOutsideFolder?: unknown;
|
|
797
864
|
};
|
|
798
865
|
tickets: {
|
|
799
866
|
number: number;
|
|
@@ -814,6 +881,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
814
881
|
activeMsBase?: number | undefined;
|
|
815
882
|
extraBudgetMinutes?: number | null | undefined;
|
|
816
883
|
pausedUntil?: unknown;
|
|
884
|
+
backgroundTasks?: unknown;
|
|
817
885
|
agentLatestVersion?: unknown;
|
|
818
886
|
skipAgentPrompt?: unknown;
|
|
819
887
|
branchHint?: unknown;
|
|
@@ -883,6 +951,20 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
883
951
|
* behaviour every runner had before this release.
|
|
884
952
|
*/
|
|
885
953
|
pausedUntil: z.ZodCatch<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
954
|
+
/**
|
|
955
|
+
* Subagents the API still believes to be alive in this session (#393).
|
|
956
|
+
*
|
|
957
|
+
* The number is the dead process's last word, kept by the API: a runner
|
|
958
|
+
* restart takes every subagent with it, and the new process seeds its own
|
|
959
|
+
* count at zero — so the descriptor is the only place the number survives
|
|
960
|
+
* long enough to be written down. The API sends it only while it still
|
|
961
|
+
* believes it (its own trust window on the count's age); absent or zero
|
|
962
|
+
* means «nothing to say», and a restore says nothing.
|
|
963
|
+
*
|
|
964
|
+
* `.catch(undefined)` like its neighbours: one malformed value must cost
|
|
965
|
+
* its own session a line at most, never the frame (QA-100 MAJOR-1).
|
|
966
|
+
*/
|
|
967
|
+
backgroundTasks: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
886
968
|
/**
|
|
887
969
|
* The newest published version of THIS session's agent — Р13, §4.8.
|
|
888
970
|
*
|
|
@@ -950,6 +1032,18 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
950
1032
|
agentProtectedBranches: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
951
1033
|
agentAllowForcePush: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
952
1034
|
agentAllowDestructiveGit: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1035
|
+
/**
|
|
1036
|
+
* #418: may the agent read and write OUTSIDE the project folder?
|
|
1037
|
+
*
|
|
1038
|
+
* `.optional().catch(undefined)` for the QA-100 MAJOR-1 reason above, and
|
|
1039
|
+
* safe here for the same reason as `agentPushBan` — though the polarity
|
|
1040
|
+
* runs the other way. `policy.ts` resolves this one with `=== true`, so a
|
|
1041
|
+
* value this schema throws away leaves the session confined to its folder:
|
|
1042
|
+
* strictly more restricted, never less. Rubbish in this field therefore
|
|
1043
|
+
* costs a project its permission, which is the direction a dropped value is
|
|
1044
|
+
* allowed to fail in.
|
|
1045
|
+
*/
|
|
1046
|
+
agentAllowOutsideFolder: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
953
1047
|
budgetUsd: z.ZodNullable<z.ZodNumber>;
|
|
954
1048
|
budgetMinutes: z.ZodNullable<z.ZodNumber>;
|
|
955
1049
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -965,6 +1059,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
965
1059
|
agentProtectedBranches?: string[] | undefined;
|
|
966
1060
|
agentAllowForcePush?: boolean | undefined;
|
|
967
1061
|
agentAllowDestructiveGit?: boolean | undefined;
|
|
1062
|
+
agentAllowOutsideFolder?: boolean | undefined;
|
|
968
1063
|
}, {
|
|
969
1064
|
path: string;
|
|
970
1065
|
id: string;
|
|
@@ -978,6 +1073,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
978
1073
|
agentProtectedBranches?: unknown;
|
|
979
1074
|
agentAllowForcePush?: unknown;
|
|
980
1075
|
agentAllowDestructiveGit?: unknown;
|
|
1076
|
+
agentAllowOutsideFolder?: unknown;
|
|
981
1077
|
}>;
|
|
982
1078
|
/**
|
|
983
1079
|
* «Run this one without the project's agent prompt.» Absent means no — both
|
|
@@ -1085,6 +1181,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1085
1181
|
agentProtectedBranches?: string[] | undefined;
|
|
1086
1182
|
agentAllowForcePush?: boolean | undefined;
|
|
1087
1183
|
agentAllowDestructiveGit?: boolean | undefined;
|
|
1184
|
+
agentAllowOutsideFolder?: boolean | undefined;
|
|
1088
1185
|
};
|
|
1089
1186
|
tickets: {
|
|
1090
1187
|
number: number;
|
|
@@ -1097,6 +1194,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1097
1194
|
} | undefined;
|
|
1098
1195
|
workMode?: "DIRECT" | "BRANCH" | undefined;
|
|
1099
1196
|
pausedUntil?: string | null | undefined;
|
|
1197
|
+
backgroundTasks?: number | undefined;
|
|
1100
1198
|
agentLatestVersion?: string | undefined;
|
|
1101
1199
|
skipAgentPrompt?: boolean | undefined;
|
|
1102
1200
|
branchHint?: string | undefined;
|
|
@@ -1126,6 +1224,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1126
1224
|
agentProtectedBranches?: unknown;
|
|
1127
1225
|
agentAllowForcePush?: unknown;
|
|
1128
1226
|
agentAllowDestructiveGit?: unknown;
|
|
1227
|
+
agentAllowOutsideFolder?: unknown;
|
|
1129
1228
|
};
|
|
1130
1229
|
tickets: {
|
|
1131
1230
|
number: number;
|
|
@@ -1146,6 +1245,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1146
1245
|
activeMsBase?: number | undefined;
|
|
1147
1246
|
extraBudgetMinutes?: number | null | undefined;
|
|
1148
1247
|
pausedUntil?: unknown;
|
|
1248
|
+
backgroundTasks?: unknown;
|
|
1149
1249
|
agentLatestVersion?: unknown;
|
|
1150
1250
|
skipAgentPrompt?: unknown;
|
|
1151
1251
|
branchHint?: unknown;
|
|
@@ -1180,6 +1280,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1180
1280
|
agentProtectedBranches?: string[] | undefined;
|
|
1181
1281
|
agentAllowForcePush?: boolean | undefined;
|
|
1182
1282
|
agentAllowDestructiveGit?: boolean | undefined;
|
|
1283
|
+
agentAllowOutsideFolder?: boolean | undefined;
|
|
1183
1284
|
};
|
|
1184
1285
|
tickets: {
|
|
1185
1286
|
number: number;
|
|
@@ -1192,6 +1293,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1192
1293
|
} | undefined;
|
|
1193
1294
|
workMode?: "DIRECT" | "BRANCH" | undefined;
|
|
1194
1295
|
pausedUntil?: string | null | undefined;
|
|
1296
|
+
backgroundTasks?: number | undefined;
|
|
1195
1297
|
agentLatestVersion?: string | undefined;
|
|
1196
1298
|
skipAgentPrompt?: boolean | undefined;
|
|
1197
1299
|
branchHint?: string | undefined;
|
|
@@ -1224,6 +1326,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1224
1326
|
agentProtectedBranches?: unknown;
|
|
1225
1327
|
agentAllowForcePush?: unknown;
|
|
1226
1328
|
agentAllowDestructiveGit?: unknown;
|
|
1329
|
+
agentAllowOutsideFolder?: unknown;
|
|
1227
1330
|
};
|
|
1228
1331
|
tickets: {
|
|
1229
1332
|
number: number;
|
|
@@ -1244,6 +1347,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1244
1347
|
activeMsBase?: number | undefined;
|
|
1245
1348
|
extraBudgetMinutes?: number | null | undefined;
|
|
1246
1349
|
pausedUntil?: unknown;
|
|
1350
|
+
backgroundTasks?: unknown;
|
|
1247
1351
|
agentLatestVersion?: unknown;
|
|
1248
1352
|
skipAgentPrompt?: unknown;
|
|
1249
1353
|
branchHint?: unknown;
|
|
@@ -1405,6 +1509,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1405
1509
|
agentProtectedBranches: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
1406
1510
|
agentAllowForcePush: z.ZodOptional<z.ZodBoolean>;
|
|
1407
1511
|
agentAllowDestructiveGit: z.ZodOptional<z.ZodBoolean>;
|
|
1512
|
+
agentAllowOutsideFolder: z.ZodOptional<z.ZodBoolean>;
|
|
1408
1513
|
}, "strip", z.ZodTypeAny, {
|
|
1409
1514
|
type: "workspace_settings";
|
|
1410
1515
|
workspaceId: string;
|
|
@@ -1414,6 +1519,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1414
1519
|
agentProtectedBranches?: string[] | undefined;
|
|
1415
1520
|
agentAllowForcePush?: boolean | undefined;
|
|
1416
1521
|
agentAllowDestructiveGit?: boolean | undefined;
|
|
1522
|
+
agentAllowOutsideFolder?: boolean | undefined;
|
|
1417
1523
|
}, {
|
|
1418
1524
|
type: "workspace_settings";
|
|
1419
1525
|
workspaceId: string;
|
|
@@ -1423,6 +1529,7 @@ export declare const GatewayFrameSchema: z.ZodDiscriminatedUnion<"type", [z.ZodO
|
|
|
1423
1529
|
agentProtectedBranches?: string[] | undefined;
|
|
1424
1530
|
agentAllowForcePush?: boolean | undefined;
|
|
1425
1531
|
agentAllowDestructiveGit?: boolean | undefined;
|
|
1532
|
+
agentAllowOutsideFolder?: boolean | undefined;
|
|
1426
1533
|
}>, z.ZodObject<{
|
|
1427
1534
|
type: z.ZodLiteral<"session_settings">;
|
|
1428
1535
|
sessionId: z.ZodString;
|
package/dist/protocol.js
CHANGED
|
@@ -59,6 +59,20 @@ export const SessionDescriptorSchema = z.object({
|
|
|
59
59
|
* behaviour every runner had before this release.
|
|
60
60
|
*/
|
|
61
61
|
pausedUntil: z.string().max(40).nullable().optional().catch(undefined),
|
|
62
|
+
/**
|
|
63
|
+
* Subagents the API still believes to be alive in this session (#393).
|
|
64
|
+
*
|
|
65
|
+
* The number is the dead process's last word, kept by the API: a runner
|
|
66
|
+
* restart takes every subagent with it, and the new process seeds its own
|
|
67
|
+
* count at zero — so the descriptor is the only place the number survives
|
|
68
|
+
* long enough to be written down. The API sends it only while it still
|
|
69
|
+
* believes it (its own trust window on the count's age); absent or zero
|
|
70
|
+
* means «nothing to say», and a restore says nothing.
|
|
71
|
+
*
|
|
72
|
+
* `.catch(undefined)` like its neighbours: one malformed value must cost
|
|
73
|
+
* its own session a line at most, never the frame (QA-100 MAJOR-1).
|
|
74
|
+
*/
|
|
75
|
+
backgroundTasks: z.number().int().min(0).optional().catch(undefined),
|
|
62
76
|
/**
|
|
63
77
|
* The newest published version of THIS session's agent — Р13, §4.8.
|
|
64
78
|
*
|
|
@@ -137,6 +151,18 @@ export const SessionDescriptorSchema = z.object({
|
|
|
137
151
|
.catch(undefined),
|
|
138
152
|
agentAllowForcePush: z.boolean().optional().catch(undefined),
|
|
139
153
|
agentAllowDestructiveGit: z.boolean().optional().catch(undefined),
|
|
154
|
+
/**
|
|
155
|
+
* #418: may the agent read and write OUTSIDE the project folder?
|
|
156
|
+
*
|
|
157
|
+
* `.optional().catch(undefined)` for the QA-100 MAJOR-1 reason above, and
|
|
158
|
+
* safe here for the same reason as `agentPushBan` — though the polarity
|
|
159
|
+
* runs the other way. `policy.ts` resolves this one with `=== true`, so a
|
|
160
|
+
* value this schema throws away leaves the session confined to its folder:
|
|
161
|
+
* strictly more restricted, never less. Rubbish in this field therefore
|
|
162
|
+
* costs a project its permission, which is the direction a dropped value is
|
|
163
|
+
* allowed to fail in.
|
|
164
|
+
*/
|
|
165
|
+
agentAllowOutsideFolder: z.boolean().optional().catch(undefined),
|
|
140
166
|
budgetUsd: z.number().nullable(),
|
|
141
167
|
budgetMinutes: z.number().nullable(),
|
|
142
168
|
}),
|
|
@@ -377,6 +403,9 @@ export const GatewayFrameSchema = z.discriminatedUnion('type', [
|
|
|
377
403
|
.optional(),
|
|
378
404
|
agentAllowForcePush: z.boolean().optional(),
|
|
379
405
|
agentAllowDestructiveGit: z.boolean().optional(),
|
|
406
|
+
// #418 — read on every file tool call, so it belongs in the live frame with
|
|
407
|
+
// its neighbours: switching it back off must not mean stopping the agent.
|
|
408
|
+
agentAllowOutsideFolder: z.boolean().optional(),
|
|
380
409
|
}),
|
|
381
410
|
z.object({
|
|
382
411
|
type: z.literal('session_settings'),
|