@yagni-app/code-staging 1.2.6-staging.1940.1 → 1.2.6-staging.1951.1
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.
|
@@ -72,6 +72,30 @@ export declare const TICKET_IMAGE_RULE: string;
|
|
|
72
72
|
* the standalone word "pi", must not open with a `- ` bullet line, no emojis.
|
|
73
73
|
*/
|
|
74
74
|
export declare const GITHUB_OPERATIONS: string;
|
|
75
|
+
/** Chrome for Testing in the sandbox image (`PI_RENDER_CHROME_DIR` in infra/e2b/pi/template.ts). */
|
|
76
|
+
export declare const SANDBOX_CHROME_PATH = "/opt/chrome/chrome";
|
|
77
|
+
/** Playwright in the sandbox image (`PI_RENDER_DIR` in infra/e2b/pi/template.ts). */
|
|
78
|
+
export declare const SANDBOX_PLAYWRIGHT_DIR = "/opt/yagni/render";
|
|
79
|
+
/**
|
|
80
|
+
* Commands {@link SANDBOX_SECTION} tells the model the sandbox does not have.
|
|
81
|
+
* The pi image smoke fails when the image gains one, as it fails when the
|
|
82
|
+
* image lacks a path the section names.
|
|
83
|
+
*/
|
|
84
|
+
export declare const SANDBOX_ABSENT_COMMANDS: readonly string[];
|
|
85
|
+
/**
|
|
86
|
+
* What a session in a YAGNI sandbox (E2B or Machines) really has, in place of
|
|
87
|
+
* {@link TICKET_IMAGE_RULE} and {@link GITHUB_OPERATIONS}, which describe a
|
|
88
|
+
* developer's laptop: there is no `gh` and no GitHub or Linear token here
|
|
89
|
+
* (by design: the sandbox has open egress and the model has read untrusted
|
|
90
|
+
* ticket text), GitHub reads go through `team_read`, the ticket's images
|
|
91
|
+
* arrive as words in its evidence pack, a browser is baked in, and installs
|
|
92
|
+
* are allowed. Every path it names is checked against the image by the pi
|
|
93
|
+
* image smoke (`assertSandboxSection` in scripts/release/smokeChecks.ts).
|
|
94
|
+
*
|
|
95
|
+
* Content constraints (parity with {@link TICKET_IMAGE_RULE}): must not
|
|
96
|
+
* contain the standalone word "pi", no `- ` bullet lines, no emojis.
|
|
97
|
+
*/
|
|
98
|
+
export declare const SANDBOX_SECTION: string;
|
|
75
99
|
/**
|
|
76
100
|
* File-tool steering: prefer the first-class write/edit tools over bash file
|
|
77
101
|
* writes, one write call per file instead of a heredoc batch, and never create
|
|
@@ -170,6 +194,12 @@ export interface BrandSystemPromptOptions {
|
|
|
170
194
|
* all-or-nothing per-surface call, not user policy.
|
|
171
195
|
*/
|
|
172
196
|
fileToolSteering?: boolean;
|
|
197
|
+
/**
|
|
198
|
+
* The session runs in a YAGNI sandbox (E2B or Machines), not on a laptop:
|
|
199
|
+
* {@link SANDBOX_SECTION} replaces the ticket-image rule and the GitHub
|
|
200
|
+
* operations section. Absent or false, the prompt is exactly the laptop's.
|
|
201
|
+
*/
|
|
202
|
+
sandbox?: boolean;
|
|
173
203
|
}
|
|
174
204
|
/**
|
|
175
205
|
* Rebrand pi's assembled system prompt as YAGNI Code's, and optionally inject a
|
|
@@ -155,6 +155,58 @@ export const GITHUB_OPERATIONS = "## GitHub Operations\n" +
|
|
|
155
155
|
"multi-paragraph.";
|
|
156
156
|
/** Stable header that starts the GitHub operations section (idempotency anchor). */
|
|
157
157
|
const GITHUB_OPERATIONS_HEADER = "## GitHub Operations";
|
|
158
|
+
/** Chrome for Testing in the sandbox image (`PI_RENDER_CHROME_DIR` in infra/e2b/pi/template.ts). */
|
|
159
|
+
export const SANDBOX_CHROME_PATH = "/opt/chrome/chrome";
|
|
160
|
+
/** Playwright in the sandbox image (`PI_RENDER_DIR` in infra/e2b/pi/template.ts). */
|
|
161
|
+
export const SANDBOX_PLAYWRIGHT_DIR = "/opt/yagni/render";
|
|
162
|
+
/**
|
|
163
|
+
* Commands {@link SANDBOX_SECTION} tells the model the sandbox does not have.
|
|
164
|
+
* The pi image smoke fails when the image gains one, as it fails when the
|
|
165
|
+
* image lacks a path the section names.
|
|
166
|
+
*/
|
|
167
|
+
export const SANDBOX_ABSENT_COMMANDS = ["gh"];
|
|
168
|
+
/** Stable header that starts the sandbox section (idempotency anchor). */
|
|
169
|
+
const SANDBOX_SECTION_HEADER = "## Your sandbox";
|
|
170
|
+
/**
|
|
171
|
+
* What a session in a YAGNI sandbox (E2B or Machines) really has, in place of
|
|
172
|
+
* {@link TICKET_IMAGE_RULE} and {@link GITHUB_OPERATIONS}, which describe a
|
|
173
|
+
* developer's laptop: there is no `gh` and no GitHub or Linear token here
|
|
174
|
+
* (by design: the sandbox has open egress and the model has read untrusted
|
|
175
|
+
* ticket text), GitHub reads go through `team_read`, the ticket's images
|
|
176
|
+
* arrive as words in its evidence pack, a browser is baked in, and installs
|
|
177
|
+
* are allowed. Every path it names is checked against the image by the pi
|
|
178
|
+
* image smoke (`assertSandboxSection` in scripts/release/smokeChecks.ts).
|
|
179
|
+
*
|
|
180
|
+
* Content constraints (parity with {@link TICKET_IMAGE_RULE}): must not
|
|
181
|
+
* contain the standalone word "pi", no `- ` bullet lines, no emojis.
|
|
182
|
+
*/
|
|
183
|
+
export const SANDBOX_SECTION = `${SANDBOX_SECTION_HEADER}\n` +
|
|
184
|
+
"\n" +
|
|
185
|
+
"This session runs in a YAGNI sandbox, not on a developer's laptop. Four things " +
|
|
186
|
+
"are true here.\n" +
|
|
187
|
+
"\n" +
|
|
188
|
+
"GitHub: this image has no `gh` command and the session has no GitHub or " +
|
|
189
|
+
"Linear token. Read GitHub through team_read with the Team's GitHub tools, " +
|
|
190
|
+
"such as `github_get_pull_request`, `github_list_pull_requests`, " +
|
|
191
|
+
"`github_list_issues` and `github_list_workflow_runs`; `github_get_file` and " +
|
|
192
|
+
"`github_search_code` read the other repositories the ticket touches (this " +
|
|
193
|
+
'one is your checkout). Call team_read with tool "list" first: it shows which ' +
|
|
194
|
+
"of these this Team can read. YAGNI pushes your branch and posts the pull " +
|
|
195
|
+
"request's comments after you finish; you do neither.\n" +
|
|
196
|
+
"\n" +
|
|
197
|
+
"Ticket images: the ticket's screenshots are not on disk here. Each one is " +
|
|
198
|
+
"described in words in the ticket's evidence pack, the \"Evidence behind the " +
|
|
199
|
+
"ticket\" section of the ticket you were given. Trust that description; a " +
|
|
200
|
+
"screenshot named there as not read could not be described.\n" +
|
|
201
|
+
"\n" +
|
|
202
|
+
`A browser: Chrome is at \`${SANDBOX_CHROME_PATH}\` and Playwright is installed ` +
|
|
203
|
+
`at \`${SANDBOX_PLAYWRIGHT_DIR}\`. Load Playwright from ` +
|
|
204
|
+
`\`${SANDBOX_PLAYWRIGHT_DIR}/node_modules/playwright\` and launch Chrome with ` +
|
|
205
|
+
`\`executablePath: "${SANDBOX_CHROME_PATH}"\` to open a page, click through it, ` +
|
|
206
|
+
"or take a screenshot.\n" +
|
|
207
|
+
"\n" +
|
|
208
|
+
"Installs: the network is open, and you may install what the work needs. The " +
|
|
209
|
+
"mission file says what to install.";
|
|
158
210
|
/**
|
|
159
211
|
* File-tool steering: prefer the first-class write/edit tools over bash file
|
|
160
212
|
* writes, one write call per file instead of a heredoc batch, and never create
|
|
@@ -364,14 +416,26 @@ export function brandSystemPrompt(original, opts = {}) {
|
|
|
364
416
|
// (so a long, noisy rules block cannot bury it) but BEFORE the closing
|
|
365
417
|
// reminder (which stays the most-recent instruction). The idempotency guard
|
|
366
418
|
// keys on the stable section header, mirroring the rules guard above.
|
|
367
|
-
|
|
419
|
+
// A sandbox session gets the "Your sandbox" section in the same place
|
|
420
|
+
// instead: both laptop sections name tools the sandbox does not have.
|
|
421
|
+
if (opts.sandbox) {
|
|
422
|
+
// A prompt an earlier pass branded for a laptop loses both laptop
|
|
423
|
+
// sections, so the two never sit beside the sandbox's.
|
|
424
|
+
s = s
|
|
425
|
+
.replace(`\n\n${TICKET_IMAGE_RULE_HEADER}\n${TICKET_IMAGE_RULE}`, "")
|
|
426
|
+
.replace(`\n\n${GITHUB_OPERATIONS}`, "");
|
|
427
|
+
if (!s.includes(SANDBOX_SECTION_HEADER)) {
|
|
428
|
+
s = `${s}\n\n${SANDBOX_SECTION}`;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
else if (!s.includes(TICKET_IMAGE_RULE_HEADER)) {
|
|
368
432
|
s = `${s}\n\n${TICKET_IMAGE_RULE_HEADER}\n${TICKET_IMAGE_RULE}`;
|
|
369
433
|
}
|
|
370
434
|
// 5b2. GitHub operations — a standing directive added alongside
|
|
371
435
|
// the ticket-image rule (same placement, same idempotency shape). It is
|
|
372
436
|
// universal knowledge, not user policy, so it rides the branded prompt
|
|
373
|
-
// instead of the repository-rules section.
|
|
374
|
-
if (!s.includes(GITHUB_OPERATIONS_HEADER)) {
|
|
437
|
+
// instead of the repository-rules section. Laptop only (see 5b).
|
|
438
|
+
if (!opts.sandbox && !s.includes(GITHUB_OPERATIONS_HEADER)) {
|
|
375
439
|
s = `${s}\n\n${GITHUB_OPERATIONS}`;
|
|
376
440
|
}
|
|
377
441
|
// 5b2b. File-tool steering — same standing-directive placement and
|
package/dist/extension/index.js
CHANGED
|
@@ -45,7 +45,7 @@ import { isSandboxDenialOutput, sandboxAutoAllowDecision, sandboxDenialSignature
|
|
|
45
45
|
import { sandboxFailureKey } from "./permission/approvedPrefixes.js";
|
|
46
46
|
import { createEscapeTally } from "./sandbox/escapeTally.js";
|
|
47
47
|
import { createGrantCensus } from "./permission/grantCensus.js";
|
|
48
|
-
import { toolchainSpawnHookFromEnv, toolchainUserBashOperations } from "./toolchain/spawnHook.js";
|
|
48
|
+
import { runsInSandboxHarness, toolchainSpawnHookFromEnv, toolchainUserBashOperations } from "./toolchain/spawnHook.js";
|
|
49
49
|
import { registerTelemetry as defaultRegisterTelemetry } from "./telemetry/register.js";
|
|
50
50
|
import { loadHooksConfig, makeHookRunner, registerHooks } from "./hooks.js";
|
|
51
51
|
import { loadPermissionRules } from "./permissionRules/loadConfig.js";
|
|
@@ -1578,6 +1578,12 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1578
1578
|
// Resolved per turn (not once at activation): /ultra can flip the driver
|
|
1579
1579
|
// between the delegation and diamond identities mid-session.
|
|
1580
1580
|
const isDriver = isDriverCaller(env);
|
|
1581
|
+
// A sandbox session is told what its sandbox has instead of a laptop's
|
|
1582
|
+
// `gh` recipes. Both signals are the ones the sandbox already rides: the
|
|
1583
|
+
// harness's toolchain file, and eval mode, which is where team_read (the
|
|
1584
|
+
// tool the section sends GitHub reads to) is registered. A local eval
|
|
1585
|
+
// compare sets eval mode without the harness, so it keeps the laptop's.
|
|
1586
|
+
const sandboxSession = evalMode && runsInSandboxHarness(env);
|
|
1581
1587
|
pi.on("before_agent_start", (event) => noBranding
|
|
1582
1588
|
? undefined
|
|
1583
1589
|
: {
|
|
@@ -1601,6 +1607,7 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1601
1607
|
scratchpadSection: scratchpadSectionText,
|
|
1602
1608
|
attributionSection: attributionPromptSection(attribution()),
|
|
1603
1609
|
fileToolSteering: condensedToolsRegistered,
|
|
1610
|
+
...(sandboxSession ? { sandbox: true } : {}),
|
|
1604
1611
|
}),
|
|
1605
1612
|
});
|
|
1606
1613
|
// Turn-lifecycle WAL: a `turn_start` with no matching `turn_end` is the
|
|
@@ -48,6 +48,13 @@ export declare function readToolchainFile(path: string, log?: Log): ToolchainFil
|
|
|
48
48
|
export declare function makeToolchainSpawnHook(file: ToolchainFile): BashSpawnHook;
|
|
49
49
|
/** The hooks applied in order (first to last), skipping absent ones; undefined when none is present. */
|
|
50
50
|
export declare function composeSpawnHooks(...hooks: Array<BashSpawnHook | undefined>): BashSpawnHook | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* This process runs under the sandbox harness (`infra/e2b/pi/pi-harness.sh`
|
|
53
|
+
* exports `YAGNI_TOOLCHAIN_FILE` on every E2B and Machines session, Reeve's
|
|
54
|
+
* and Wright's alike, and `/go` children inherit it). A developer's own
|
|
55
|
+
* terminal never sets it.
|
|
56
|
+
*/
|
|
57
|
+
export declare function runsInSandboxHarness(env?: NodeJS.ProcessEnv): boolean;
|
|
51
58
|
/**
|
|
52
59
|
* The hook for this process: present only when `YAGNI_TOOLCHAIN_FILE` names a
|
|
53
60
|
* readable, well-formed file. Unset is silent (not a sandbox); set but
|
|
@@ -97,6 +97,15 @@ export function composeSpawnHooks(...hooks) {
|
|
|
97
97
|
return present[0];
|
|
98
98
|
return (ctx) => present.reduce((acc, hook) => hook(acc), ctx);
|
|
99
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* This process runs under the sandbox harness (`infra/e2b/pi/pi-harness.sh`
|
|
102
|
+
* exports `YAGNI_TOOLCHAIN_FILE` on every E2B and Machines session, Reeve's
|
|
103
|
+
* and Wright's alike, and `/go` children inherit it). A developer's own
|
|
104
|
+
* terminal never sets it.
|
|
105
|
+
*/
|
|
106
|
+
export function runsInSandboxHarness(env = process.env) {
|
|
107
|
+
return Boolean(env.YAGNI_TOOLCHAIN_FILE?.trim());
|
|
108
|
+
}
|
|
100
109
|
/**
|
|
101
110
|
* The hook for this process: present only when `YAGNI_TOOLCHAIN_FILE` names a
|
|
102
111
|
* readable, well-formed file. Unset is silent (not a sandbox); set but
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yagni-app/code-staging",
|
|
3
|
-
"version": "1.2.6-staging.
|
|
3
|
+
"version": "1.2.6-staging.1951.1",
|
|
4
4
|
"description": "YAGNI Code: a terminal coding agent that already knows your company. One YAGNI login routes the model and grounds the agent in your team's context.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": "YAGNI, Inc. <jack@yagni.app> (https://yagni.app)",
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"turndown": "^7.2.4",
|
|
59
59
|
"typebox": "^1.3.15"
|
|
60
60
|
},
|
|
61
|
-
"yagniSourceSha": "
|
|
61
|
+
"yagniSourceSha": "0985f34f33f3c59e0fb7ed50af520abbcbb2466c"
|
|
62
62
|
}
|