@bridge4dev/runner 0.30.0 → 0.33.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/dist/adapters/claude.js +162 -45
- package/dist/adapters/codex.js +42 -14
- package/dist/adapters/types.d.ts +25 -1
- package/dist/agent-prompt.d.ts +75 -0
- package/dist/agent-prompt.js +252 -0
- package/dist/index.js +23 -0
- package/dist/policy.d.ts +73 -1
- package/dist/policy.js +398 -53
- package/dist/protocol.d.ts +240 -30
- package/dist/protocol.js +56 -0
- package/dist/supervisor.d.ts +29 -1
- package/dist/supervisor.js +186 -5
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/protocol.js
CHANGED
|
@@ -52,9 +52,52 @@ export const SessionDescriptorSchema = z.object({
|
|
|
52
52
|
* every project the moment a runner updated ahead of its API.
|
|
53
53
|
*/
|
|
54
54
|
agentAutoCommit: z.boolean().optional(),
|
|
55
|
+
/**
|
|
56
|
+
* The project's own prompt file, relative to `path` — its contents reach
|
|
57
|
+
* the agent as SYSTEM-prompt text (see `agent-prompt.ts` for why the level
|
|
58
|
+
* matters).
|
|
59
|
+
*
|
|
60
|
+
* `.catch(undefined)` for the same reason as `branchHint` and `branchPlan`:
|
|
61
|
+
* this field travels inside `hello_ack`, which carries EVERY session of the
|
|
62
|
+
* server, so a malformed value must cost its own session's prompt at most —
|
|
63
|
+
* never the frame (QA-100 MAJOR-1). The runner re-validates the path from
|
|
64
|
+
* scratch anyway; the bounds here only keep nonsense off the wire.
|
|
65
|
+
*/
|
|
66
|
+
agentPromptPath: z.string().max(300).nullable().optional().catch(undefined),
|
|
67
|
+
/**
|
|
68
|
+
* Session 18: the project's git policy for agents.
|
|
69
|
+
*
|
|
70
|
+
* `.optional().catch(undefined)` for the QA-100 MAJOR-1 reason above — this
|
|
71
|
+
* rides inside `hello_ack` with every session on the server, so a malformed
|
|
72
|
+
* value must cost its own session's policy at most, never the frame.
|
|
73
|
+
*
|
|
74
|
+
* «Costs its own session's policy» is safe here BECAUSE of the polarity in
|
|
75
|
+
* `policy.ts`: `agentPushBan` resolves `undefined` to «refuse», so a value
|
|
76
|
+
* this schema drops leaves the session strictly more restricted, never less.
|
|
77
|
+
* That property is the whole reason `.catch(undefined)` is acceptable on a
|
|
78
|
+
* security field, and it is why the resolution lives in exactly one
|
|
79
|
+
* function rather than at each point of use.
|
|
80
|
+
*/
|
|
81
|
+
agentPushBan: z.boolean().optional().catch(undefined),
|
|
82
|
+
agentProtectedBranches: z
|
|
83
|
+
.array(z
|
|
84
|
+
.string()
|
|
85
|
+
.max(200)
|
|
86
|
+
.regex(/^[A-Za-z0-9][A-Za-z0-9._\-/]*$/))
|
|
87
|
+
.max(20)
|
|
88
|
+
.optional()
|
|
89
|
+
.catch(undefined),
|
|
90
|
+
agentAllowForcePush: z.boolean().optional().catch(undefined),
|
|
91
|
+
agentAllowDestructiveGit: z.boolean().optional().catch(undefined),
|
|
55
92
|
budgetUsd: z.number().nullable(),
|
|
56
93
|
budgetMinutes: z.number().nullable(),
|
|
57
94
|
}),
|
|
95
|
+
/**
|
|
96
|
+
* «Run this one without the project's agent prompt.» Absent means no — both
|
|
97
|
+
* because an older API never sends it and because that is the default a human
|
|
98
|
+
* gets when they do not touch the checkbox.
|
|
99
|
+
*/
|
|
100
|
+
skipAgentPrompt: z.boolean().optional().catch(undefined),
|
|
58
101
|
tickets: z.array(z.object({ id: z.string().uuid(), number: z.number(), title: z.string() })),
|
|
59
102
|
// Branch the API would like this session to use (derived from its ticket
|
|
60
103
|
// group). Optional: an older API omits it and the runner keeps its
|
|
@@ -212,6 +255,19 @@ export const GatewayFrameSchema = z.discriminatedUnion('type', [
|
|
|
212
255
|
workspaceId: z.string().min(1).max(64),
|
|
213
256
|
trustMode: z.enum(['STRICT', 'NORMAL', 'AUTO']).optional(),
|
|
214
257
|
agentAutoCommit: z.boolean().optional(),
|
|
258
|
+
// Session 18 — the git policy, for the same reason: read on every tool
|
|
259
|
+
// call, so switching «Принудительно запретить push» back on must not
|
|
260
|
+
// require stopping the agent first.
|
|
261
|
+
agentPushBan: z.boolean().optional(),
|
|
262
|
+
agentProtectedBranches: z
|
|
263
|
+
.array(z
|
|
264
|
+
.string()
|
|
265
|
+
.max(200)
|
|
266
|
+
.regex(/^[A-Za-z0-9][A-Za-z0-9._\-/]*$/))
|
|
267
|
+
.max(20)
|
|
268
|
+
.optional(),
|
|
269
|
+
agentAllowForcePush: z.boolean().optional(),
|
|
270
|
+
agentAllowDestructiveGit: z.boolean().optional(),
|
|
215
271
|
}),
|
|
216
272
|
z.object({
|
|
217
273
|
type: z.literal('session_settings'),
|
package/dist/supervisor.d.ts
CHANGED
|
@@ -118,6 +118,26 @@ export declare class Supervisor {
|
|
|
118
118
|
* stay queued rather than be marked delivered (session 9).
|
|
119
119
|
*/
|
|
120
120
|
private launchAgent;
|
|
121
|
+
/**
|
|
122
|
+
* The project's own prompt file, read fresh for THIS agent process.
|
|
123
|
+
*
|
|
124
|
+
* Read per launch rather than per session on purpose: a system prompt only
|
|
125
|
+
* ever changes when the process restarts, so «edit the file, then press
|
|
126
|
+
* Continue» is the honest contract, and re-reading is what makes it true.
|
|
127
|
+
*
|
|
128
|
+
* Every outcome is said out loud. The whole point of moving the project's
|
|
129
|
+
* rules into the system prompt is that they can no longer be quietly
|
|
130
|
+
* outranked — so «the prompt did not load» must never be indistinguishable
|
|
131
|
+
* from «the prompt loaded». Supervisor notices repeat on every launch (only
|
|
132
|
+
* ADAPTER notices are de-duplicated — gotcha #148), which is exactly what is
|
|
133
|
+
* wanted here: each agent process either has the prompt or does not.
|
|
134
|
+
*
|
|
135
|
+
* Returns the text AND the absolute file, because layer 1 needs the second to
|
|
136
|
+
* refuse writes to it: in `workMode: DIRECT` the project folder is the
|
|
137
|
+
* agent's own working directory, so without that rule a session could rewrite
|
|
138
|
+
* the prompt it will itself be started with next time (QA-130 MAJOR-3).
|
|
139
|
+
*/
|
|
140
|
+
private resolveAgentPrompt;
|
|
121
141
|
/** Warn the user when this share of the budget is gone. */
|
|
122
142
|
private static readonly BUDGET_WARN_RATIO;
|
|
123
143
|
/** Is the agent actually working right now (i.e. should the clock run)? */
|
|
@@ -408,6 +428,14 @@ export declare function composeInitialPrompt(descriptor: SessionDescriptor): str
|
|
|
408
428
|
* A repository that wants both agents equipped ships both files, or symlinks
|
|
409
429
|
* one to the other. That is a repository convention and not something a runner
|
|
410
430
|
* should paper over.
|
|
431
|
+
*
|
|
432
|
+
* `agentPrompt` is the one exception, and it is an exception for a reason this
|
|
433
|
+
* function cannot do anything about: `CLAUDE.md` and `AGENTS.md` arrive at the
|
|
434
|
+
* USER level, and a project's standing process rules have to sit at the system
|
|
435
|
+
* level to survive both a compaction and a rule the CLI states about itself
|
|
436
|
+
* (`agent-prompt.ts`). It arrives here already read and already checked — this
|
|
437
|
+
* function still opens no files — and it goes LAST, so a project overrides us
|
|
438
|
+
* exactly the way `CLAUDE.md` does by being read last.
|
|
411
439
|
*/
|
|
412
|
-
export declare function composeWorkspaceContext(descriptor: SessionDescriptor): string;
|
|
440
|
+
export declare function composeWorkspaceContext(descriptor: SessionDescriptor, agentPrompt?: string): string;
|
|
413
441
|
//# sourceMappingURL=supervisor.d.ts.map
|
package/dist/supervisor.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { log } from './log.js';
|
|
4
4
|
import { claimAutoResume, clearAutoResume, pruneAutoResume } from './auto-resume.js';
|
|
5
5
|
import { evaluateRecipeCommand, maskSecrets, maskString } from './policy.js';
|
|
6
|
+
import { agentPromptSizeLabel, quotePath, readAgentPrompt } from './agent-prompt.js';
|
|
6
7
|
import { JournalStore } from './journal.js';
|
|
7
8
|
import { deleteSessionBranch, ensurePreviewWorktree, ensureSessionWorktree, prepareDirectWorkspace, previewWorktreePath, removePreviewWorktree, removeSessionWorktree, repoKeyFor, sessionWorktreePath, validateWorkspacePath, } from './git.js';
|
|
8
9
|
import { readRecipeProposal } from './recipe.js';
|
|
@@ -21,6 +22,28 @@ import { availableModes, MODE_REFUSED_TEXT } from './adapters/types.js';
|
|
|
21
22
|
/** Refusals shared by every checkpoint command (ticket #126). */
|
|
22
23
|
const CHECKPOINTS_OFF = 'Restore points are switched off on this server ([checkpoints] enabled = false)';
|
|
23
24
|
const AGENT_BUSY = 'The agent is still working — stop the turn first';
|
|
25
|
+
/**
|
|
26
|
+
* The project's git policy, lifted out of a session descriptor (session 18).
|
|
27
|
+
*
|
|
28
|
+
* One function so that «which descriptor fields are the git policy» is answered
|
|
29
|
+
* once. Absent fields are passed through as absent — `resolveGitPolicy` in
|
|
30
|
+
* `policy.ts` is the single place that decides what absent MEANS, and inventing
|
|
31
|
+
* a default here would put a second such place in the codebase, facing the
|
|
32
|
+
* other way.
|
|
33
|
+
*/
|
|
34
|
+
function gitPolicyOf(descriptor) {
|
|
35
|
+
const w = descriptor.workspace;
|
|
36
|
+
return {
|
|
37
|
+
...(w.agentPushBan !== undefined ? { agentPushBan: w.agentPushBan } : {}),
|
|
38
|
+
...(w.agentProtectedBranches !== undefined
|
|
39
|
+
? { agentProtectedBranches: w.agentProtectedBranches }
|
|
40
|
+
: {}),
|
|
41
|
+
...(w.agentAllowForcePush !== undefined ? { agentAllowForcePush: w.agentAllowForcePush } : {}),
|
|
42
|
+
...(w.agentAllowDestructiveGit !== undefined
|
|
43
|
+
? { agentAllowDestructiveGit: w.agentAllowDestructiveGit }
|
|
44
|
+
: {}),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
24
47
|
export class Supervisor {
|
|
25
48
|
ws;
|
|
26
49
|
opts;
|
|
@@ -130,11 +153,43 @@ export class Supervisor {
|
|
|
130
153
|
if (frame.agentAutoCommit !== undefined) {
|
|
131
154
|
running.descriptor.workspace.agentAutoCommit = frame.agentAutoCommit;
|
|
132
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Session 18: the git policy, applied live for the same reason as the
|
|
158
|
+
* two above — it is read on every tool call, so «switch push back
|
|
159
|
+
* off» must not mean «stop the agent first».
|
|
160
|
+
*
|
|
161
|
+
* The four fields move together, and the API sends all four in every
|
|
162
|
+
* frame that touches any of them, so a partial update cannot leave
|
|
163
|
+
* the runner with «push is allowed now» beside a protected list from
|
|
164
|
+
* before the change. `!== undefined` on the FRAME is what tells a
|
|
165
|
+
* policy change apart from a trust-only frame; inside the object,
|
|
166
|
+
* `undefined` keeps its restrictive meaning as everywhere else.
|
|
167
|
+
*/
|
|
168
|
+
const policyChanged = frame.agentPushBan !== undefined ||
|
|
169
|
+
frame.agentProtectedBranches !== undefined ||
|
|
170
|
+
frame.agentAllowForcePush !== undefined ||
|
|
171
|
+
frame.agentAllowDestructiveGit !== undefined;
|
|
172
|
+
if (policyChanged) {
|
|
173
|
+
const next = {
|
|
174
|
+
...(frame.agentPushBan !== undefined ? { agentPushBan: frame.agentPushBan } : {}),
|
|
175
|
+
...(frame.agentProtectedBranches !== undefined
|
|
176
|
+
? { agentProtectedBranches: frame.agentProtectedBranches }
|
|
177
|
+
: {}),
|
|
178
|
+
...(frame.agentAllowForcePush !== undefined
|
|
179
|
+
? { agentAllowForcePush: frame.agentAllowForcePush }
|
|
180
|
+
: {}),
|
|
181
|
+
...(frame.agentAllowDestructiveGit !== undefined
|
|
182
|
+
? { agentAllowDestructiveGit: frame.agentAllowDestructiveGit }
|
|
183
|
+
: {}),
|
|
184
|
+
};
|
|
185
|
+
Object.assign(running.descriptor.workspace, next);
|
|
186
|
+
}
|
|
133
187
|
running.session?.setWorkspacePolicy({
|
|
134
188
|
...(frame.trustMode !== undefined ? { trustMode: frame.trustMode } : {}),
|
|
135
189
|
...(frame.agentAutoCommit !== undefined
|
|
136
190
|
? { agentAutoCommit: frame.agentAutoCommit }
|
|
137
191
|
: {}),
|
|
192
|
+
...(policyChanged ? { gitPolicy: gitPolicyOf(running.descriptor) } : {}),
|
|
138
193
|
});
|
|
139
194
|
}
|
|
140
195
|
break;
|
|
@@ -380,9 +435,11 @@ export class Supervisor {
|
|
|
380
435
|
return false;
|
|
381
436
|
}
|
|
382
437
|
running.lastPrompt = prompt;
|
|
383
|
-
// Facts about this session only
|
|
384
|
-
// each agent itself — see
|
|
385
|
-
|
|
438
|
+
// Facts about this session only, plus the one file the project named. The
|
|
439
|
+
// rest of the project's documentation is read by each agent itself — see
|
|
440
|
+
// `composeWorkspaceContext`.
|
|
441
|
+
const agentPrompt = this.resolveAgentPrompt(running);
|
|
442
|
+
const workspaceContext = composeWorkspaceContext(descriptor, agentPrompt?.text);
|
|
386
443
|
const rewind = running.rewindAnchor;
|
|
387
444
|
delete running.rewindAnchor;
|
|
388
445
|
// A rewind resumes the conversation the POINT names, which is not always
|
|
@@ -396,10 +453,18 @@ export class Supervisor {
|
|
|
396
453
|
cwd: running.worktreePath,
|
|
397
454
|
...(prompt ? { prompt } : {}),
|
|
398
455
|
...(workspaceContext ? { workspaceContext } : {}),
|
|
456
|
+
// Only when it was actually read: layer 1 must refuse writes to the file
|
|
457
|
+
// this process was given, not to a path it was merely told about.
|
|
458
|
+
...(agentPrompt ? { agentPromptFile: agentPrompt.absPath } : {}),
|
|
399
459
|
trustMode: descriptor.workspace.trustMode,
|
|
400
460
|
...(descriptor.workspace.agentAutoCommit === undefined
|
|
401
461
|
? {}
|
|
402
462
|
: { agentAutoCommit: descriptor.workspace.agentAutoCommit }),
|
|
463
|
+
// Session 18. Always present, even when every field inside it is absent:
|
|
464
|
+
// an absent OBJECT and an object of absent fields resolve identically
|
|
465
|
+
// (`resolveGitPolicy` gives both the restrictive reading), and passing it
|
|
466
|
+
// unconditionally keeps one code path instead of two.
|
|
467
|
+
gitPolicy: gitPolicyOf(descriptor),
|
|
403
468
|
mode: running.mode,
|
|
404
469
|
...(running.model ? { model: running.model } : {}),
|
|
405
470
|
...(running.effort ? { effort: running.effort } : {}),
|
|
@@ -426,6 +491,70 @@ export class Supervisor {
|
|
|
426
491
|
void this.pumpEvents(running);
|
|
427
492
|
return true;
|
|
428
493
|
}
|
|
494
|
+
/**
|
|
495
|
+
* The project's own prompt file, read fresh for THIS agent process.
|
|
496
|
+
*
|
|
497
|
+
* Read per launch rather than per session on purpose: a system prompt only
|
|
498
|
+
* ever changes when the process restarts, so «edit the file, then press
|
|
499
|
+
* Continue» is the honest contract, and re-reading is what makes it true.
|
|
500
|
+
*
|
|
501
|
+
* Every outcome is said out loud. The whole point of moving the project's
|
|
502
|
+
* rules into the system prompt is that they can no longer be quietly
|
|
503
|
+
* outranked — so «the prompt did not load» must never be indistinguishable
|
|
504
|
+
* from «the prompt loaded». Supervisor notices repeat on every launch (only
|
|
505
|
+
* ADAPTER notices are de-duplicated — gotcha #148), which is exactly what is
|
|
506
|
+
* wanted here: each agent process either has the prompt or does not.
|
|
507
|
+
*
|
|
508
|
+
* Returns the text AND the absolute file, because layer 1 needs the second to
|
|
509
|
+
* refuse writes to it: in `workMode: DIRECT` the project folder is the
|
|
510
|
+
* agent's own working directory, so without that rule a session could rewrite
|
|
511
|
+
* the prompt it will itself be started with next time (QA-130 MAJOR-3).
|
|
512
|
+
*/
|
|
513
|
+
resolveAgentPrompt(running) {
|
|
514
|
+
const { descriptor } = running;
|
|
515
|
+
const configured = descriptor.workspace.agentPromptPath?.trim();
|
|
516
|
+
if (!configured)
|
|
517
|
+
return undefined;
|
|
518
|
+
// The session was started with «without the project's agent prompt». Said
|
|
519
|
+
// out loud too: a session behaving unlike every other session on the
|
|
520
|
+
// project should carry the reason in its own feed.
|
|
521
|
+
if (descriptor.skipAgentPrompt) {
|
|
522
|
+
this.sendEvent(running, 'notice', {
|
|
523
|
+
level: 'info',
|
|
524
|
+
text: 'Project prompt is switched off for this session. DevBridge rules still apply.',
|
|
525
|
+
});
|
|
526
|
+
return undefined;
|
|
527
|
+
}
|
|
528
|
+
const result = readAgentPrompt(descriptor.workspace.path, configured);
|
|
529
|
+
if (!result.ok) {
|
|
530
|
+
log.warn('agent prompt not loaded', {
|
|
531
|
+
sessionId: descriptor.id,
|
|
532
|
+
path: configured,
|
|
533
|
+
reason: result.reason,
|
|
534
|
+
});
|
|
535
|
+
this.sendEvent(running, 'notice', {
|
|
536
|
+
level: 'warn',
|
|
537
|
+
text: `Project prompt ${quotePath(configured)} was NOT loaded: ${result.reason}. The agent is running without it.`,
|
|
538
|
+
});
|
|
539
|
+
return undefined;
|
|
540
|
+
}
|
|
541
|
+
log.info('agent prompt loaded', {
|
|
542
|
+
sessionId: descriptor.id,
|
|
543
|
+
path: result.relPath,
|
|
544
|
+
bytes: result.bytes,
|
|
545
|
+
sha: result.sha,
|
|
546
|
+
});
|
|
547
|
+
// The `sha` is in the line a person reads, not only in journald. It is the
|
|
548
|
+
// one signal that the file behind an unchanged path has changed — which is
|
|
549
|
+
// exactly what an agent editing its own rules in DIRECT mode looks like
|
|
550
|
+
// (QA-130 MAJOR-3), and layer 1 cannot promise to prevent that in every
|
|
551
|
+
// mode.
|
|
552
|
+
this.sendEvent(running, 'notice', {
|
|
553
|
+
level: 'info',
|
|
554
|
+
text: `Project prompt loaded from ${quotePath(result.relPath)} — ${agentPromptSizeLabel(result.bytes)}, sha ${result.sha}.`,
|
|
555
|
+
});
|
|
556
|
+
return { text: result.text, absPath: result.absPath };
|
|
557
|
+
}
|
|
429
558
|
// ─── Time budget (session 7) ───────────────────────────────────────
|
|
430
559
|
//
|
|
431
560
|
// The budget measures the AGENT's working time, not the calendar. Everything
|
|
@@ -3276,20 +3405,70 @@ export function composeInitialPrompt(descriptor) {
|
|
|
3276
3405
|
* A repository that wants both agents equipped ships both files, or symlinks
|
|
3277
3406
|
* one to the other. That is a repository convention and not something a runner
|
|
3278
3407
|
* should paper over.
|
|
3408
|
+
*
|
|
3409
|
+
* `agentPrompt` is the one exception, and it is an exception for a reason this
|
|
3410
|
+
* function cannot do anything about: `CLAUDE.md` and `AGENTS.md` arrive at the
|
|
3411
|
+
* USER level, and a project's standing process rules have to sit at the system
|
|
3412
|
+
* level to survive both a compaction and a rule the CLI states about itself
|
|
3413
|
+
* (`agent-prompt.ts`). It arrives here already read and already checked — this
|
|
3414
|
+
* function still opens no files — and it goes LAST, so a project overrides us
|
|
3415
|
+
* exactly the way `CLAUDE.md` does by being read last.
|
|
3279
3416
|
*/
|
|
3280
|
-
export function composeWorkspaceContext(descriptor) {
|
|
3417
|
+
export function composeWorkspaceContext(descriptor, agentPrompt) {
|
|
3281
3418
|
const sections = [];
|
|
3282
3419
|
const plan = descriptor.branchPlan;
|
|
3420
|
+
/**
|
|
3421
|
+
* What this session may do about push, in one sentence (session 18).
|
|
3422
|
+
*
|
|
3423
|
+
* `!== false` — silence refuses, exactly as `resolveGitPolicy` reads it. This
|
|
3424
|
+
* text and the rule in `policy.ts` have to agree; an agent told it may push
|
|
3425
|
+
* and then refused has been lied to, and an agent told it may not while the
|
|
3426
|
+
* project allows it wastes the freedom the owner paid for.
|
|
3427
|
+
*/
|
|
3428
|
+
const pushLine = (() => {
|
|
3429
|
+
// `?.` and not `.`: this function is exported and called with descriptors
|
|
3430
|
+
// assembled by hand in tests and by older code paths. A missing `workspace`
|
|
3431
|
+
// must land on the refusing branch like every other unknown here, not throw
|
|
3432
|
+
// and take the whole session start with it.
|
|
3433
|
+
if (descriptor.workspace?.agentPushBan !== false) {
|
|
3434
|
+
return '- You cannot push: `git push` is refused for this project. A human presses «Push» in DevBridge when they want the branch on the remote.';
|
|
3435
|
+
}
|
|
3436
|
+
const guarded = descriptor.workspace?.agentProtectedBranches ?? ['main', 'master'];
|
|
3437
|
+
return guarded.length > 0
|
|
3438
|
+
? `- You may push, except to these protected branches: ${guarded.join(', ')}. Name the branch explicitly — \`git push <remote> <branch>\`.`
|
|
3439
|
+
: '- You may push.';
|
|
3440
|
+
})();
|
|
3283
3441
|
if (plan) {
|
|
3284
3442
|
const forked = plan.baseBranch ? ` It was branched from \`${plan.baseBranch}\`.` : '';
|
|
3285
3443
|
sections.push([
|
|
3286
3444
|
'Git in this session:',
|
|
3287
3445
|
`- You are in a dedicated worktree on branch \`${plan.branch}\`.${forked}`,
|
|
3288
3446
|
'- Commit to this branch. Do not switch branches and do not merge into the base branch yourself — a human presses «Apply» in DevBridge, which squash-merges your branch for them.',
|
|
3289
|
-
|
|
3447
|
+
pushLine,
|
|
3290
3448
|
`- Put a plan or working notes in \`docs/devbridge/${plan.branch.replace(/\//g, '-')}.md\`, unless this repository already has its own convention for where such documents live — if it does, follow that.`,
|
|
3291
3449
|
].join('\n'));
|
|
3292
3450
|
}
|
|
3451
|
+
else {
|
|
3452
|
+
/**
|
|
3453
|
+
* A DIRECT session got NO «Git in this session» block at all, and that is
|
|
3454
|
+
* the older half of this bug (session 16 shipped DIRECT as the default;
|
|
3455
|
+
* this block stayed gated on `branchPlan`, which only a BRANCH session
|
|
3456
|
+
* has). So the arrangement that writes into the person's OWN project
|
|
3457
|
+
* folder was the one told nothing about where it was — while the adapter's
|
|
3458
|
+
* static line went on claiming «a dedicated git worktree on a session
|
|
3459
|
+
* branch», which in DIRECT is simply false.
|
|
3460
|
+
*
|
|
3461
|
+
* Being in somebody's working copy is the case that needs saying MORE, not
|
|
3462
|
+
* less: there is no «Apply» to undo it, and uncommitted changes in that
|
|
3463
|
+
* folder may not be the agent's.
|
|
3464
|
+
*/
|
|
3465
|
+
sections.push([
|
|
3466
|
+
'Git in this session:',
|
|
3467
|
+
'- You are working in the PROJECT FOLDER ITSELF, on the branch it is already checked out on — not in a worktree of your own. There is no session branch and nothing to «apply» afterwards: your commits are immediately on the branch the person works on.',
|
|
3468
|
+
'- Uncommitted changes you find here may be a human’s, or another session’s. Never discard or reset what you did not write.',
|
|
3469
|
+
pushLine,
|
|
3470
|
+
].join('\n'));
|
|
3471
|
+
}
|
|
3293
3472
|
// The ticket convention lives HERE, not in the first chat message (session
|
|
3294
3473
|
// 16). It is a standing rule of the project — true on the twentieth turn as
|
|
3295
3474
|
// much as the first — and putting it in the visible prompt only trained the
|
|
@@ -3305,6 +3484,8 @@ export function composeWorkspaceContext(descriptor) {
|
|
|
3305
3484
|
else if (descriptor.tickets.length > 0) {
|
|
3306
3485
|
sections.push('DevBridge tickets are attached to this chat for CONTEXT only. Read them with the DevBridge MCP tools; do not change their status — this session is not assigned to them.');
|
|
3307
3486
|
}
|
|
3487
|
+
if (agentPrompt)
|
|
3488
|
+
sections.push(agentPrompt);
|
|
3308
3489
|
return sections.join('\n\n');
|
|
3309
3490
|
}
|
|
3310
3491
|
//# sourceMappingURL=supervisor.js.map
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const RUNNER_VERSION = "0.
|
|
1
|
+
export declare const RUNNER_VERSION = "0.33.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
package/package.json
CHANGED