@yagni-app/code-staging 1.1.4-staging.1416.1 → 1.1.4-staging.1419.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.
package/dist/extension/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import { makeRecordDecisionTool } from "./recordDecisionTool.js";
|
|
|
17
17
|
import { makeSuggestNextWorkTool } from "./nextWorkTool.js";
|
|
18
18
|
import { BRAND_NAME, brandSystemPrompt, brandingDisabled, buildMastheadString, YAGNI_IDENTITY_DRIVER, YAGNI_IDENTITY_DRIVER_GROUNDING_FREE, YAGNI_IDENTITY_GROUNDING_FREE, YAGNI_IDENTITY_ULTRA, YAGNI_IDENTITY_ULTRA_GROUNDING_FREE } from "./branding.js";
|
|
19
19
|
import { claudeRulesSection } from "./claudeRules.js";
|
|
20
|
-
import { ensureScratchpadDir, SCRATCHPAD_TMPDIR_ENV, scratchpadDir as scratchpadDirFor, scratchpadSection } from "./scratchpad.js";
|
|
20
|
+
import { ensureScratchpadDir, SCRATCHPAD_TMPDIR_ENV, scratchpadDir as scratchpadDirFor, scratchpadOwnerRootFor, scratchpadSection } from "./scratchpad.js";
|
|
21
21
|
import { registerCostCommand } from "./costHud.js";
|
|
22
22
|
import { isDebug } from "./diagnostics.js";
|
|
23
23
|
import { logEvent } from "./errorSink.js";
|
|
@@ -1339,24 +1339,69 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1339
1339
|
// so the very first turn already carries the section. Fails closed: no
|
|
1340
1340
|
// sessionId (a bare pi run) or a failed mkdir means no section, and the mkdir
|
|
1341
1341
|
// failure is logged so a missing scratchpad is not silent.
|
|
1342
|
+
const scratchpadTmpRoot = env[SCRATCHPAD_TMPDIR_ENV] || undefined;
|
|
1342
1343
|
const scratchpadDirPath = scratchpadDirFor({
|
|
1343
1344
|
sessionId: env.YAGNI_SESSION_ID ?? undefined,
|
|
1344
1345
|
cwd: process.cwd(),
|
|
1345
|
-
tmp:
|
|
1346
|
+
tmp: scratchpadTmpRoot,
|
|
1346
1347
|
});
|
|
1347
1348
|
let scratchpadSectionText;
|
|
1348
1349
|
if (scratchpadDirPath) {
|
|
1349
|
-
const ensured = ensureScratchpadDir(scratchpadDirPath
|
|
1350
|
+
const ensured = ensureScratchpadDir(scratchpadDirPath, undefined, {
|
|
1351
|
+
onError: (errorCode) => {
|
|
1352
|
+
logEvent({
|
|
1353
|
+
source: "scratchpad",
|
|
1354
|
+
level: "error",
|
|
1355
|
+
event: "scratchpad_mkdir_failed",
|
|
1356
|
+
sessionId: env.YAGNI_SESSION_ID ?? undefined,
|
|
1357
|
+
fields: { path: scratchpadDirPath, errorCode },
|
|
1358
|
+
});
|
|
1359
|
+
},
|
|
1360
|
+
});
|
|
1350
1361
|
if (ensured) {
|
|
1351
1362
|
scratchpadSectionText = scratchpadSection(ensured);
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1363
|
+
// Claude Code parity: srt reads CLAUDE_CODE_TMPDIR from THIS process's
|
|
1364
|
+
// env when wrapping a sandboxed bash command and forces the child's
|
|
1365
|
+
// TMPDIR onto it. Point it at the per-user scratchpad OWNER ROOT (not
|
|
1366
|
+
// the scratchpad itself) so sandboxed-bash temp files land in the same
|
|
1367
|
+
// per-user tree the scratchpad lives in, under a path SHORT enough for
|
|
1368
|
+
// named unix-socket binds (the test-runner IPC pattern — a deep
|
|
1369
|
+
// scratchpad TMPDIR trips macOS's 104-char sun_path limit with EINVAL).
|
|
1370
|
+
// The scratchpad remains the sanctioned staging place by PRIORITY (the
|
|
1371
|
+
// prompt section outranks $TMPDIR), not by path convergence.
|
|
1372
|
+
//
|
|
1373
|
+
// Production note: children (subagents, /go stages) inherit this env
|
|
1374
|
+
// because `env` IS process.env there and the runner spawns with it. A
|
|
1375
|
+
// harness injecting deps.env sees the write only in that object —
|
|
1376
|
+
// spawned children of such a harness do not, by construction of the
|
|
1377
|
+
// runner's own env seam.
|
|
1378
|
+
//
|
|
1379
|
+
// Unset when there is no scratchpad (no session id, or the mkdir
|
|
1380
|
+
// failed) — srt keeps its /tmp/claude default and nothing changes for
|
|
1381
|
+
// those sessions.
|
|
1382
|
+
if (!env.CLAUDE_CODE_TMPDIR) {
|
|
1383
|
+
const ownerRoot = scratchpadOwnerRootFor({ tmp: scratchpadTmpRoot });
|
|
1384
|
+
env.CLAUDE_CODE_TMPDIR = ownerRoot;
|
|
1385
|
+
logEvent({
|
|
1386
|
+
source: "scratchpad",
|
|
1387
|
+
level: "info",
|
|
1388
|
+
event: "scratchpad_tmpdir_pinned",
|
|
1389
|
+
sessionId: env.YAGNI_SESSION_ID ?? undefined,
|
|
1390
|
+
fields: { path: ownerRoot },
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
else {
|
|
1394
|
+
// The trail must distinguish "pinned" from "respected an operator
|
|
1395
|
+
// override" — no path value here (the explicit value is the
|
|
1396
|
+
// operator's, not ours to echo).
|
|
1397
|
+
logEvent({
|
|
1398
|
+
source: "scratchpad",
|
|
1399
|
+
level: "info",
|
|
1400
|
+
event: "scratchpad_tmpdir_pin_skipped",
|
|
1401
|
+
sessionId: env.YAGNI_SESSION_ID ?? undefined,
|
|
1402
|
+
fields: {},
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1360
1405
|
}
|
|
1361
1406
|
}
|
|
1362
1407
|
// The condensed Claude Code-style transcript: the seven built-ins re-render
|
|
@@ -118,8 +118,10 @@ export function makeBashComposition(manager, settings, cwd, onShellResolutionRet
|
|
|
118
118
|
const heredocNote = "Caveat: heredocs (<<EOF) write temp files relative to the current directory on macOS bash 3.2 — from an unwritable cwd they fail; prefer printf or redirect from a file in the working directory.";
|
|
119
119
|
// $TMPDIR guidance (Claude Code parity, always-on): the sandbox sets
|
|
120
120
|
// TMPDIR to the per-user sandbox-writable temp dir; /tmp itself is NOT
|
|
121
|
-
// writable.
|
|
122
|
-
|
|
121
|
+
// writable. When the system prompt names a scratchpad directory, that
|
|
122
|
+
// directory takes priority for temporary files — stated here so the two
|
|
123
|
+
// guidance lines never read as mutually exclusive always-rules.
|
|
124
|
+
const tmpdirNote = "For temporary files, use the $TMPDIR environment variable unless your system prompt names a scratchpad directory — the scratchpad takes priority for temporary files. TMPDIR is automatically set to the correct sandbox-writable directory in sandbox mode. Do NOT use /tmp directly - use $TMPDIR instead.";
|
|
123
125
|
const wrapped = {
|
|
124
126
|
...def,
|
|
125
127
|
description: `${def.description}\n\n## Command sandbox\nCommands run inside an OS sandbox: ${restrictions.join("; ")}. ${strictNote}\n${heredocNote}\n${tmpdirNote}`,
|
|
@@ -48,6 +48,18 @@ export declare function scratchpadDir(opts?: {
|
|
|
48
48
|
uid?: number;
|
|
49
49
|
tmp?: string;
|
|
50
50
|
}): string | null;
|
|
51
|
+
/**
|
|
52
|
+
* PURE: the per-user scratchpad OWNER ROOT, recomputed from the SAME inputs
|
|
53
|
+
* `scratchpadDir` resolves from (never derived by stripping segments off a
|
|
54
|
+
* built path — a layout change would silently shift a stripped spelling).
|
|
55
|
+
* The value the sandbox forces TMPDIR onto (Claude parity): per-user, shared
|
|
56
|
+
* across sessions, and short enough for named unix-socket binds where the
|
|
57
|
+
* deep scratchpad path would trip macOS's 104-char sun_path limit.
|
|
58
|
+
*/
|
|
59
|
+
export declare function scratchpadOwnerRootFor(opts?: {
|
|
60
|
+
uid?: number;
|
|
61
|
+
tmp?: string;
|
|
62
|
+
}): string;
|
|
51
63
|
/**
|
|
52
64
|
* IMPURE: ensure the scratchpad dir exists (owner-only), failing soft. Returns
|
|
53
65
|
* the path on success and null on failure — a null result means "no scratchpad
|
|
@@ -56,7 +68,9 @@ export declare function scratchpadDir(opts?: {
|
|
|
56
68
|
export declare function ensureScratchpadDir(path: string, mkdir?: (p: string, o: {
|
|
57
69
|
mode: number;
|
|
58
70
|
recursive: boolean;
|
|
59
|
-
}) => void
|
|
71
|
+
}) => void, opts?: {
|
|
72
|
+
onError?: (errorCode: string) => void;
|
|
73
|
+
}): string | null;
|
|
60
74
|
/**
|
|
61
75
|
* PURE: the prompt section naming the scratchpad. Gated by the caller on the
|
|
62
76
|
* dir existing; when present, it tells the agent where to put intermediate
|
|
@@ -61,17 +61,39 @@ export function scratchpadDir(opts = {}) {
|
|
|
61
61
|
const cwd = sanitizeCwdSegment(opts.cwd ?? ".");
|
|
62
62
|
return join(tmp, scratchpadOwnerDir(uid), cwd, sessionId, "scratchpad");
|
|
63
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* PURE: the per-user scratchpad OWNER ROOT, recomputed from the SAME inputs
|
|
66
|
+
* `scratchpadDir` resolves from (never derived by stripping segments off a
|
|
67
|
+
* built path — a layout change would silently shift a stripped spelling).
|
|
68
|
+
* The value the sandbox forces TMPDIR onto (Claude parity): per-user, shared
|
|
69
|
+
* across sessions, and short enough for named unix-socket binds where the
|
|
70
|
+
* deep scratchpad path would trip macOS's 104-char sun_path limit.
|
|
71
|
+
*/
|
|
72
|
+
export function scratchpadOwnerRootFor(opts = {}) {
|
|
73
|
+
const tmp = opts.tmp ?? tmpdir();
|
|
74
|
+
const uid = opts.uid ?? (typeof process.getuid === "function" ? process.getuid() ?? 0 : 0);
|
|
75
|
+
return join(tmp, scratchpadOwnerDir(uid));
|
|
76
|
+
}
|
|
64
77
|
/**
|
|
65
78
|
* IMPURE: ensure the scratchpad dir exists (owner-only), failing soft. Returns
|
|
66
79
|
* the path on success and null on failure — a null result means "no scratchpad
|
|
67
80
|
* this session", which the caller turns into an omitted prompt section.
|
|
68
81
|
*/
|
|
69
|
-
export function ensureScratchpadDir(path, mkdir = mkdirSync) {
|
|
82
|
+
export function ensureScratchpadDir(path, mkdir = mkdirSync, opts = {}) {
|
|
70
83
|
try {
|
|
71
84
|
mkdir(path, { recursive: true, mode: 0o700 });
|
|
72
85
|
return path;
|
|
73
86
|
}
|
|
74
|
-
catch {
|
|
87
|
+
catch (err) {
|
|
88
|
+
// The errno CODE (never the thrown message — the message can carry a
|
|
89
|
+
// path or system detail, and the caller forwards this to the sink).
|
|
90
|
+
// fs errors are plain Error instances whose distinguishing value is
|
|
91
|
+
// .code (ENOTDIR vs EACCES vs EPERM...); the constructor name would
|
|
92
|
+
// always read "Error" and distinguish nothing. The fallback keeps the
|
|
93
|
+
// class name for codeless throws (a non-fs throw), so the callback's
|
|
94
|
+
// value is "errno code, or class name when there is no code".
|
|
95
|
+
const code = err?.code;
|
|
96
|
+
opts.onError?.(code ?? (err instanceof Error ? err.constructor.name : typeof err));
|
|
75
97
|
return null;
|
|
76
98
|
}
|
|
77
99
|
}
|
|
@@ -82,12 +104,13 @@ export function ensureScratchpadDir(path, mkdir = mkdirSync) {
|
|
|
82
104
|
*/
|
|
83
105
|
export function scratchpadSection(dir) {
|
|
84
106
|
return ("# Scratchpad directory\n\n" +
|
|
85
|
-
`
|
|
107
|
+
`IMPORTANT: Always use this scratchpad directory for temporary files instead of /tmp or other system temp directories (including $TMPDIR in sandboxed bash):\n` +
|
|
86
108
|
`${dir}\n\n` +
|
|
109
|
+
"Use this directory for ALL temporary file needs:\n" +
|
|
87
110
|
"- Store intermediate results or data during multi-step tasks.\n" +
|
|
88
111
|
"- Write temporary scripts or configuration files.\n" +
|
|
89
112
|
"- Save outputs that don't belong in the user's project.\n" +
|
|
90
|
-
"-
|
|
113
|
+
"- Staging files for subagents: paths in this directory are readable by every subagent of this session, so hand subagents scratchpad paths rather than $TMPDIR or /tmp paths.\n\n" +
|
|
91
114
|
"The directory is session-specific and isolated from the user's project.");
|
|
92
115
|
}
|
|
93
116
|
//# sourceMappingURL=scratchpad.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yagni-app/code-staging",
|
|
3
|
-
"version": "1.1.4-staging.
|
|
3
|
+
"version": "1.1.4-staging.1419.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": "72326f4909f8b24a2657ae8ddd9caa4f4ad2e614"
|
|
62
62
|
}
|