@yagni-app/code-staging 1.2.6-staging.2011.1 → 1.2.6-staging.2020.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.
|
@@ -173,8 +173,8 @@ export { recordDecision } from "./recordDecisionTool.js";
|
|
|
173
173
|
export type { RecordDecisionParams } from "./recordDecisionTool.js";
|
|
174
174
|
export { runInitPass, runTeamSetup, registerTeamSetupCommand, isFreshWorkspace, readRepoIntake, draftEngineering, summarizeDraft, FRESH_BRIEF_MIN_CHARS, } from "./initPass.js";
|
|
175
175
|
export type { RunInitPassDeps, InitPassOutcome, RunTeamSetupDeps, TeamSetupOutcome, RepoIntake, RepoIntakeFs, EngineeringDraft, DraftTeam, } from "./initPass.js";
|
|
176
|
-
export { isInitDone, markInitDone, initDoneMarkerFile, _setInitDoneHomeForTest } from "./initDone.js";
|
|
177
|
-
export { brandSystemPrompt, YAGNI_IDENTITY, YAGNI_IDENTITY_DRIVER, YAGNI_IDENTITY_ULTRA, BRAND_NAME } from "./branding.js";
|
|
176
|
+
export { isInitDone, markInitDone, initDoneMarkerFile, _setInitDoneHomeForTest, } from "./initDone.js";
|
|
177
|
+
export { brandSystemPrompt, YAGNI_IDENTITY, YAGNI_IDENTITY_DRIVER, YAGNI_IDENTITY_ULTRA, BRAND_NAME, } from "./branding.js";
|
|
178
178
|
export { loadHooksConfig, makeHookRunner, registerHooks, matchesMatcher, parsePreToolUseOutput, parsePermissionRequestOutput, parseAdditionalContext, parseCompactCancel, HOOK_SUPPORTED_EVENTS, } from "./hooks.js";
|
|
179
179
|
export type { HookEntry, HookGroup, HooksConfig, HookEventName, HookRunner, PreToolUseHookResult, PermissionRequestHookResult, RegisterHooksDeps, } from "./hooks.js";
|
|
180
180
|
export { createUltraHolder, registerUltraCommand } from "./ultra.js";
|
|
@@ -188,16 +188,16 @@ export { runPipeline } from "./pipeline/orchestrator.js";
|
|
|
188
188
|
export type { RunPipelineDeps } from "./pipeline/orchestrator.js";
|
|
189
189
|
export { HEADLESS_GO_EXIT, HEADLESS_GO_USAGE, parseHeadlessGoArgs, runHeadlessGo, validateHeadlessGoArgs, } from "./pipeline/headlessGo.js";
|
|
190
190
|
export type { HeadlessGoArgs, HeadlessGoDeps, HeadlessGoOutcome, HeadlessGoResult, HeadlessStageRow, } from "./pipeline/headlessGo.js";
|
|
191
|
-
export { isMissionMode, missionSeed, missionSkippedStages, normalizeMission } from "./pipeline/mission.js";
|
|
191
|
+
export { isMissionMode, missionSeed, missionSkippedStages, normalizeMission, } from "./pipeline/mission.js";
|
|
192
192
|
export type { MissionInputs } from "./pipeline/mission.js";
|
|
193
193
|
export { clampTier, parseTierCap, resolveTierCap, TIER_CAP_ENV } from "./pipeline/tierCap.js";
|
|
194
194
|
export { withResilience, classifyTransient, composeAbortSignal } from "./pipeline/resilience.js";
|
|
195
|
-
export type { ResilienceAttemptRecord, ResilienceSeams, RunStageFn } from "./pipeline/resilience.js";
|
|
195
|
+
export type { ResilienceAttemptRecord, ResilienceSeams, RunStageFn, } from "./pipeline/resilience.js";
|
|
196
196
|
export { DEFAULT_RESILIENCE_POLICY } from "./pipeline/types.js";
|
|
197
197
|
export type { ResiliencePolicy } from "./pipeline/types.js";
|
|
198
|
-
export { buildVerifyEnv, detectVerifyCommand, makeRunVerify, parseVerifyFailures } from "./pipeline/verify.js";
|
|
198
|
+
export { buildVerifyEnv, detectVerifyCommand, makeRunVerify, parseVerifyFailures, } from "./pipeline/verify.js";
|
|
199
199
|
export type { VerifyCommand, VerifyOutcome } from "./pipeline/verify.js";
|
|
200
|
-
export { blindStages, reportOnlyStages, makeGroundedVsBlindEval, formatComparisonReport } from "./pipeline/eval.js";
|
|
200
|
+
export { blindStages, reportOnlyStages, makeGroundedVsBlindEval, formatComparisonReport, } from "./pipeline/eval.js";
|
|
201
201
|
export type { ComparisonReport, LaneFit, LaneOutcome } from "./pipeline/eval.js";
|
|
202
202
|
export { registerGoCompareCommand } from "./pipeline/goCompareCommand.js";
|
|
203
203
|
export { registerSubagents, makeSubagentTool, discoverSubagents, parseAgentMarkdown, buildSubagentStage, formatAgentList, mapModelTier, SUBAGENT_TOOL_NAME, GENERAL_AGENT_NAME, MAX_PARALLEL_SUBAGENTS, MAX_PARALLEL_SUBAGENTS_ULTRA, DEFAULT_SUBAGENT_TOOLS, } from "./subagents.js";
|
|
@@ -211,9 +211,9 @@ export type { PermissionMode, PermissionPolicy, GateDecision, RegisterPermission
|
|
|
211
211
|
export { makeBlessStore, blessPath } from "./bless.js";
|
|
212
212
|
export type { BlessStore, BlessRule } from "./bless.js";
|
|
213
213
|
export { bankDecision, fetchDecisions, supersedeDecision, parseDecideArgs, formatDecisionsList, shortId, registerDecisionCommands, DEFAULT_LIST_LIMIT, } from "./decisions.js";
|
|
214
|
-
export type { DecisionClientOpts, DecisionListItem, BankDecisionParams, DecideArgs } from "./decisions.js";
|
|
214
|
+
export type { DecisionClientOpts, DecisionListItem, BankDecisionParams, DecideArgs, } from "./decisions.js";
|
|
215
215
|
export { makeDecisionCapture, CAPTURE_DEBOUNCE_MS } from "./decisionCapture.js";
|
|
216
|
-
export type { DecisionCapture, DecisionCaptureDeps, BlessCaptureInfo, CaptureCtx } from "./decisionCapture.js";
|
|
216
|
+
export type { DecisionCapture, DecisionCaptureDeps, BlessCaptureInfo, CaptureCtx, } from "./decisionCapture.js";
|
|
217
217
|
export { registerAmbientRecall, fetchRecall, formatRecallBlock, toRepoRelativePath, normalizeRecall, RECALL_TIMEOUT_MS, RECALL_MIN_DECISIONS, } from "./recall.js";
|
|
218
218
|
export type { RecallResult, RecallClientOpts, RegisterRecallDeps } from "./recall.js";
|
|
219
219
|
export { makeCostAccumulator, formatCostLine, registerCostCommand, cacheHitPercent, spendTokenTotals, } from "./costHud.js";
|
|
@@ -231,5 +231,5 @@ export type { RowKind, ToolRow, ToolRowPatch } from "./toolRuns.js";
|
|
|
231
231
|
export { registerWorkingLine, composeWorkingMessage, NULL_WORKING_LINE, WORKING_INDICATOR_FRAMES, WORKING_VERBS, } from "./workingLine.js";
|
|
232
232
|
export type { WorkingLineHandle, RegisterWorkingLineDeps } from "./workingLine.js";
|
|
233
233
|
export { crashReportsDisabled, installNonZeroExitReporter, installUncaughtExceptionMonitor, makeCrashReporter, reportFatalCrash, sanitizeCrashError, sanitizeCrashText, } from "./crashReport.js";
|
|
234
|
-
export type { CrashReporter, CrashReporterOpts, FatalCrashOpts, SanitizedCrash } from "./crashReport.js";
|
|
234
|
+
export type { CrashReporter, CrashReporterOpts, FatalCrashOpts, SanitizedCrash, } from "./crashReport.js";
|
|
235
235
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/extension/index.js
CHANGED
|
@@ -2,9 +2,9 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { Text } from "@earendil-works/pi-tui";
|
|
3
3
|
import { DEFAULT_ADVISOR_LIMITS, formatAdvisorSubtotal, makeAdvisorState } from "./advisor.js";
|
|
4
4
|
import { makeChildUsageState } from "./childUsage.js";
|
|
5
|
-
import { appendGrant, createGrantsRefresher, loadGrants, resolveRepoKey, storagePrefix } from "./permission/approvedPrefixes.js";
|
|
5
|
+
import { appendGrant, createGrantsRefresher, loadGrants, resolveRepoKey, storagePrefix, } from "./permission/approvedPrefixes.js";
|
|
6
6
|
import { redactCommand } from "./redact.js";
|
|
7
|
-
import { formatGuardianSubtotal, GUARDIAN_MODEL_TIER, makeGuardianState, resolveGuardianLimits, reviewCommand, deriveGuardianTimeoutMs } from "./permission/guardian.js";
|
|
7
|
+
import { formatGuardianSubtotal, GUARDIAN_MODEL_TIER, makeGuardianState, resolveGuardianLimits, reviewCommand, deriveGuardianTimeoutMs, } from "./permission/guardian.js";
|
|
8
8
|
import { makeAskAdvisorTool, registerAdviseCommand } from "./askAdvisorTool.js";
|
|
9
9
|
import { makeAskUserQuestionTool } from "./askUserQuestionTool.js";
|
|
10
10
|
import { makeAskYagniTool } from "./askYagniTool.js";
|
|
@@ -18,9 +18,9 @@ import { makePrototypeVerifyTool } from "./prototypeVerify.js";
|
|
|
18
18
|
import { makeRecordDecisionTool } from "./recordDecisionTool.js";
|
|
19
19
|
import { makeSuggestNextWorkTool } from "./nextWorkTool.js";
|
|
20
20
|
import { makePrototypeCreateTool, makePrototypeAttachTool, makePrototypeReadTool, makePrototypeDraftTool, makePrototypeVerificationTool, makePrototypeReviseTool, makePrototypeListTool, makePrototypeShareTool, } from "./prototypeTool.js";
|
|
21
|
-
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";
|
|
21
|
+
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";
|
|
22
22
|
import { claudeRulesSection } from "./claudeRules.js";
|
|
23
|
-
import { ensureScratchpadDir, SCRATCHPAD_TMPDIR_ENV, scratchpadDir as scratchpadDirFor, scratchpadOwnerRootFor, scratchpadSection } from "./scratchpad.js";
|
|
23
|
+
import { ensureScratchpadDir, SCRATCHPAD_TMPDIR_ENV, scratchpadDir as scratchpadDirFor, scratchpadOwnerRootFor, scratchpadSection, } from "./scratchpad.js";
|
|
24
24
|
import { registerCostCommand } from "./costHud.js";
|
|
25
25
|
import { isDebug } from "./diagnostics.js";
|
|
26
26
|
import { logEvent } from "./errorSink.js";
|
|
@@ -29,25 +29,26 @@ import { droppedSessionRuns, sessionRunIds } from "./sessionRuns.js";
|
|
|
29
29
|
import { codeStateHome } from "./stateHome.js";
|
|
30
30
|
import { mutateLocalConfig } from "./settingsFiles.js";
|
|
31
31
|
import { logTurnLifecycle } from "./turnLog.js";
|
|
32
|
-
import { createYagniFooterFactory, cyclePermissionMode, formatCwd, GIT_MUTATING_PATTERN, isShiftTab } from "./footer.js";
|
|
32
|
+
import { createYagniFooterFactory, cyclePermissionMode, formatCwd, GIT_MUTATING_PATTERN, isShiftTab, } from "./footer.js";
|
|
33
33
|
import { RerouteNotifier } from "./rerouteNotice.js";
|
|
34
|
-
import { isFreshWorkspace, registerTeamSetupCommand, runInitPass as defaultRunInitPass } from "./initPass.js";
|
|
35
|
-
import { isInitDone as defaultIsInitDone, markInitDone as defaultMarkInitDone } from "./initDone.js";
|
|
34
|
+
import { isFreshWorkspace, registerTeamSetupCommand, runInitPass as defaultRunInitPass, } from "./initPass.js";
|
|
35
|
+
import { isInitDone as defaultIsInitDone, markInitDone as defaultMarkInitDone, } from "./initDone.js";
|
|
36
36
|
import { looksMutating } from "./mcp/tools.js";
|
|
37
|
-
import { startMcp as defaultStartMcp, wireShutdown, deriveStartupConnectivityNotices } from "./mcp/startup.js";
|
|
37
|
+
import { startMcp as defaultStartMcp, wireShutdown, deriveStartupConnectivityNotices, } from "./mcp/startup.js";
|
|
38
38
|
import { registerGoCommand } from "./pipeline/goCommand.js";
|
|
39
39
|
import { registerGoCompareCommand } from "./pipeline/goCompareCommand.js";
|
|
40
|
-
import { DEFAULT_PERMISSION_POLICY, createModeHolder, registerPermissionGate } from "./permission/gate.js";
|
|
40
|
+
import { DEFAULT_PERMISSION_POLICY, createModeHolder, registerPermissionGate, } from "./permission/gate.js";
|
|
41
41
|
import { setBgJobOutcomeReporter } from "./bgJobs.js";
|
|
42
42
|
import { registerBgJobsUi } from "./bgJobsRender.js";
|
|
43
43
|
import { registerSandbox } from "./sandbox/session.js";
|
|
44
|
-
import { isSandboxDenialOutput, sandboxAutoAllowDecision, sandboxDenialSignature, shouldUseSandbox } from "./sandbox/bash.js";
|
|
44
|
+
import { isSandboxDenialOutput, sandboxAutoAllowDecision, sandboxDenialSignature, shouldUseSandbox, } from "./sandbox/bash.js";
|
|
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 { runsInSandboxHarness, toolchainSpawnHookFromEnv, toolchainUserBashOperations } from "./toolchain/spawnHook.js";
|
|
48
|
+
import { runsInSandboxHarness, toolchainBashToolDefinition, toolchainSpawnHookFromEnv, toolchainUserBashOperations, } from "./toolchain/spawnHook.js";
|
|
49
49
|
import { CLOUD_SESSION_ENV } from "./pipeline/cloud.js";
|
|
50
50
|
import { registerStageClock } from "./pipeline/stageClock.js";
|
|
51
|
+
import { scrubSecrets } from "./pipeline/scrubSecrets.js";
|
|
51
52
|
import { registerTelemetry as defaultRegisterTelemetry } from "./telemetry/register.js";
|
|
52
53
|
import { loadHooksConfig, makeHookRunner, registerHooks } from "./hooks.js";
|
|
53
54
|
import { loadPermissionRules } from "./permissionRules/loadConfig.js";
|
|
@@ -63,11 +64,11 @@ import { registerAmbientRecall } from "./recall.js";
|
|
|
63
64
|
import { resilientFetch } from "./resilientFetch.js";
|
|
64
65
|
import { installNonZeroExitReporter, installUncaughtExceptionMonitor, makeCrashReporter, runningUnderTest, } from "./crashReport.js";
|
|
65
66
|
import { createToolOutcomeBatcher } from "./toolOutcomes.js";
|
|
66
|
-
import { flushSpool as defaultFlushSpool } from "./spool.js";
|
|
67
|
+
import { flushSpool as defaultFlushSpool, } from "./spool.js";
|
|
67
68
|
import { makeAuthedFetch, makeTokenProvider } from "./tokenProvider.js";
|
|
68
69
|
import { attributionHeaders, fetchCatalog as defaultFetchCatalog, fetchContextBrief as defaultFetchContextBrief, getToken, getTokenExpiresAt as defaultGetTokenExpiresAt, getWorkspaceId as defaultGetWorkspaceId, isDriverCaller, resolveBaseUrl, tokenExpiryNotice, } from "./config.js";
|
|
69
70
|
import { buildYagniProvider } from "./provider.js";
|
|
70
|
-
import { orderDriverCatalog, registerStartTierGuard, registerTierCommand, trailReason } from "./tierCommand.js";
|
|
71
|
+
import { orderDriverCatalog, registerStartTierGuard, registerTierCommand, trailReason, } from "./tierCommand.js";
|
|
71
72
|
import { makeModelPickerListener } from "./modelPicker.js";
|
|
72
73
|
import { registerChipEditor } from "./chipEditor.js";
|
|
73
74
|
import { registerSlashCommandFilter } from "./slashCommandFilter.js";
|
|
@@ -160,6 +161,17 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
160
161
|
const fetchCatalog = deps.fetchCatalog ?? defaultFetchCatalog;
|
|
161
162
|
const env = deps.env ?? process.env;
|
|
162
163
|
const evalMode = isEvalMode(env);
|
|
164
|
+
// A stage child of `yagni go --headless --cloud` (the one-attempt lane):
|
|
165
|
+
// headlessGo sets the variable where it parses the flag, and the lane's
|
|
166
|
+
// launch line exports the toolchain file. It runs outside eval mode, so it
|
|
167
|
+
// is told its sandbox and gets team_read on these two signals alone; the
|
|
168
|
+
// flag outside a sandbox changes nothing.
|
|
169
|
+
const cloudSession = runsInSandboxHarness(env) && env[CLOUD_SESSION_ENV] === "1";
|
|
170
|
+
// A sandbox session is told what its sandbox has instead of a laptop's
|
|
171
|
+
// `gh` recipes: a cloud session, or a legacy build or scoping session (eval
|
|
172
|
+
// mode under the harness, where team_read is registered). A local eval
|
|
173
|
+
// compare sets eval mode without the harness, so it keeps the laptop's.
|
|
174
|
+
const sandboxSession = cloudSession || (evalMode && runsInSandboxHarness(env));
|
|
163
175
|
// A timed /go stage child refuses every tool once its time is up. Order does
|
|
164
176
|
// not matter: pi runs every tool_call handler until one blocks, and an allow
|
|
165
177
|
// (a result without `block`) never ends the loop, so this block always lands.
|
|
@@ -238,7 +250,9 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
238
250
|
logAttributionProvenance(attributionState.load);
|
|
239
251
|
return;
|
|
240
252
|
}
|
|
241
|
-
attributionState.load = loadAttributionSettings(undefined, ctx.cwd || process.cwd(), env, {
|
|
253
|
+
attributionState.load = loadAttributionSettings(undefined, ctx.cwd || process.cwd(), env, {
|
|
254
|
+
trusted,
|
|
255
|
+
});
|
|
242
256
|
logAttributionProvenance(attributionState.load);
|
|
243
257
|
};
|
|
244
258
|
// The refreshing TokenProvider replaces the old boot-time token snapshot:
|
|
@@ -279,7 +293,12 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
279
293
|
// like every other external side effect; injectable for tests.
|
|
280
294
|
const authReporter = deps.authReporter ??
|
|
281
295
|
(!evalMode
|
|
282
|
-
? makeCrashReporter({
|
|
296
|
+
? makeCrashReporter({
|
|
297
|
+
baseUrl,
|
|
298
|
+
getToken: getTokenFn,
|
|
299
|
+
fetchImpl: deps.fetchImpl,
|
|
300
|
+
env: deps.env,
|
|
301
|
+
})
|
|
283
302
|
: async () => { });
|
|
284
303
|
// YAG-500 Fix A+C: the model-path 401 recovery outcome, set by the
|
|
285
304
|
// message_end handler so it can produce the right user-facing message. The
|
|
@@ -372,8 +391,9 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
372
391
|
// standard-tier extraction, replacing the bash + curl + python dance.
|
|
373
392
|
pi.registerTool(makeWebFetchTool(toolOpts));
|
|
374
393
|
// The Team's connected tools, read through YAGNI as the Team. Only a run's
|
|
375
|
-
// sandbox holds the run token the route requires, so only it gets the tool
|
|
376
|
-
|
|
394
|
+
// sandbox holds the run token the route requires, so only it gets the tool:
|
|
395
|
+
// a legacy build or scoping session (eval mode) or a cloud session.
|
|
396
|
+
if (evalMode || cloudSession) {
|
|
377
397
|
pi.registerTool(makeTeamReadTool(toolOpts));
|
|
378
398
|
}
|
|
379
399
|
// Ticket write-back (spec 2026-08-09): explicit user-intent writes to the
|
|
@@ -397,7 +417,12 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
397
417
|
// model escalation, not a judgment read); the consult just runs BLIND —
|
|
398
418
|
// the existing grounding-free advisor persona + the ask_yagni-free tool
|
|
399
419
|
// list — so no recorded decision reaches the consult either.
|
|
400
|
-
const askAdvisorTool = makeAskAdvisorTool({
|
|
420
|
+
const askAdvisorTool = makeAskAdvisorTool({
|
|
421
|
+
state: advisorState,
|
|
422
|
+
workingLine,
|
|
423
|
+
childUsage,
|
|
424
|
+
grounded: grounding,
|
|
425
|
+
});
|
|
401
426
|
pi.registerTool(askAdvisorTool);
|
|
402
427
|
// /advise runs the SAME tool, sharing the state handle, so a manual consult
|
|
403
428
|
// draws on the same cap rather than opening a side channel around it.
|
|
@@ -464,7 +489,12 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
464
489
|
// driver's delegation identity for the diamond directive (see the
|
|
465
490
|
// before_agent_start handler below) and widens the tool's fan-out ceiling.
|
|
466
491
|
const ultraHolder = createUltraHolder();
|
|
467
|
-
registerSubagents(pi, {
|
|
492
|
+
registerSubagents(pi, {
|
|
493
|
+
isUltra: () => ultraHolder.get(),
|
|
494
|
+
workingLine,
|
|
495
|
+
childUsage,
|
|
496
|
+
grounded: grounding,
|
|
497
|
+
});
|
|
468
498
|
// /plugin + /reload-plugins: manage Claude Code marketplace plugins from the
|
|
469
499
|
// session, driving the launcher's `yagni plugin …` (YAGNI_CLI_PATH).
|
|
470
500
|
registerPluginPanel(pi, { env: deps.env, runCli: deps.runPluginCli });
|
|
@@ -497,7 +527,17 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
497
527
|
// client estimate on a throw, a non-2xx, or a malformed body.
|
|
498
528
|
fetchRunSpend: async (runId, signal) => {
|
|
499
529
|
try {
|
|
500
|
-
const res = await resilientFetch(buildRunSpendUrl(baseUrl, runId), { method: "GET", headers: { authorization: `Bearer ${getTokenFn() ?? ""}` } }, {
|
|
530
|
+
const res = await resilientFetch(buildRunSpendUrl(baseUrl, runId), { method: "GET", headers: { authorization: `Bearer ${getTokenFn() ?? ""}` } }, {
|
|
531
|
+
fetchImpl: authedFetch,
|
|
532
|
+
signal,
|
|
533
|
+
policy: {
|
|
534
|
+
maxAttempts: 1,
|
|
535
|
+
backoffBaseMs: 0,
|
|
536
|
+
backoffMaxMs: 0,
|
|
537
|
+
timeoutMs: COST_FETCH_TIMEOUT_MS,
|
|
538
|
+
jitterRatio: 0,
|
|
539
|
+
},
|
|
540
|
+
});
|
|
501
541
|
if (!res.ok)
|
|
502
542
|
return null;
|
|
503
543
|
return parseSpendResponse(await res.json());
|
|
@@ -658,7 +698,9 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
658
698
|
try {
|
|
659
699
|
ctx?.ui?.notify?.(summary, "info");
|
|
660
700
|
}
|
|
661
|
-
catch {
|
|
701
|
+
catch {
|
|
702
|
+
/* fail-soft */
|
|
703
|
+
}
|
|
662
704
|
logEvent({
|
|
663
705
|
source: "sandbox",
|
|
664
706
|
level: "info",
|
|
@@ -686,7 +728,10 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
686
728
|
}
|
|
687
729
|
await toolOutcomes.close();
|
|
688
730
|
});
|
|
689
|
-
const footerInvalidateHandle = {
|
|
731
|
+
const footerInvalidateHandle = {
|
|
732
|
+
invalidateGit: () => { },
|
|
733
|
+
requestRender: () => { },
|
|
734
|
+
};
|
|
690
735
|
const guardianState = makeGuardianState();
|
|
691
736
|
// Disabled by the local env override OR the workspace kill switch
|
|
692
737
|
// (yagni_code.guardian, read from the catalog response at launch). The env
|
|
@@ -703,8 +748,12 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
703
748
|
// hard-aborting at 15s and preempting the backend's retry. Absent an
|
|
704
749
|
// advertisement (older backend) this keeps the default.
|
|
705
750
|
const guardianTimeoutMs = deriveGuardianTimeoutMs(guardianLimits, {
|
|
706
|
-
...(guardianTimeoutAdvertisedMs !== undefined
|
|
707
|
-
|
|
751
|
+
...(guardianTimeoutAdvertisedMs !== undefined
|
|
752
|
+
? { timeoutMs: guardianTimeoutAdvertisedMs }
|
|
753
|
+
: {}),
|
|
754
|
+
...(guardianMaxAttemptsAdvertised !== undefined
|
|
755
|
+
? { maxAttempts: guardianMaxAttemptsAdvertised }
|
|
756
|
+
: {}),
|
|
708
757
|
});
|
|
709
758
|
guardianLimits.timeoutMs = guardianTimeoutMs;
|
|
710
759
|
// YAG-510: Guardian events go to the unified sink under source:"guardian"
|
|
@@ -760,7 +809,13 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
760
809
|
body: JSON.stringify(body),
|
|
761
810
|
}, {
|
|
762
811
|
fetchImpl: authedFetch,
|
|
763
|
-
policy: {
|
|
812
|
+
policy: {
|
|
813
|
+
maxAttempts: 1,
|
|
814
|
+
backoffBaseMs: 0,
|
|
815
|
+
backoffMaxMs: 0,
|
|
816
|
+
timeoutMs: GUARDIAN_EVENT_TIMEOUT_MS,
|
|
817
|
+
jitterRatio: 0,
|
|
818
|
+
},
|
|
764
819
|
});
|
|
765
820
|
if (!res.ok) {
|
|
766
821
|
logEvent({
|
|
@@ -841,7 +896,9 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
841
896
|
}
|
|
842
897
|
return { key, value };
|
|
843
898
|
},
|
|
844
|
-
clear: () => {
|
|
899
|
+
clear: () => {
|
|
900
|
+
map.clear();
|
|
901
|
+
},
|
|
845
902
|
};
|
|
846
903
|
};
|
|
847
904
|
const sandboxedFailures = makeSandboxFailureMap(() => true);
|
|
@@ -881,7 +938,12 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
881
938
|
? { rules: [], diagnostics: { warnings: [], neverConsultedTools: [] } }
|
|
882
939
|
: loadPermissionRules({ cwd: process.cwd() });
|
|
883
940
|
for (const w of loadedRules.diagnostics.warnings) {
|
|
884
|
-
logEvent({
|
|
941
|
+
logEvent({
|
|
942
|
+
source: "permission-rules",
|
|
943
|
+
level: "warn",
|
|
944
|
+
event: "config_warning",
|
|
945
|
+
fields: { warning: w },
|
|
946
|
+
});
|
|
885
947
|
}
|
|
886
948
|
if (loadedRules.diagnostics.neverConsultedTools.length > 0) {
|
|
887
949
|
logEvent({
|
|
@@ -916,8 +978,10 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
916
978
|
// A build sandbox's toolchain (spec 2026-10-08 §1): when the harness names a
|
|
917
979
|
// readable toolchain file, every bash spawn and `!` command runs on the
|
|
918
980
|
// repository's PATH and variables while this process stays on the image's
|
|
919
|
-
// Node. Composed before the sandbox's proxy hook.
|
|
920
|
-
|
|
981
|
+
// Node. Composed before the sandbox's proxy hook. Eval mode too: a legacy
|
|
982
|
+
// build runs in it, and its shell needs the repository's toolchain as much
|
|
983
|
+
// as a lane session's. Absent everywhere else.
|
|
984
|
+
const toolchainHook = toolchainSpawnHookFromEnv(env);
|
|
921
985
|
const sandboxHandle = evalMode
|
|
922
986
|
? null
|
|
923
987
|
: registerSandbox(pi, {
|
|
@@ -954,7 +1018,8 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
954
1018
|
// ignores the flag entirely — the command runs wrapped, no ask.
|
|
955
1019
|
sandboxEscape: (toolName, params) => toolName === "bash" &&
|
|
956
1020
|
sandboxHandle.manager.initialized &&
|
|
957
|
-
params.dangerouslyDisableSandbox ===
|
|
1021
|
+
params.dangerouslyDisableSandbox ===
|
|
1022
|
+
true &&
|
|
958
1023
|
sandboxHandle.settings().allowUnsandboxedCommands !== false,
|
|
959
1024
|
// The auto-sandbox rung's would-run-wrapped proof: after the gate
|
|
960
1025
|
// strips the flag, this command must actually be wrapped by
|
|
@@ -1031,7 +1096,9 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1031
1096
|
// gate at activation): toggling it mid-session does nothing until the
|
|
1032
1097
|
// session restarts. When set, the gate seeds from the L1 ladder only,
|
|
1033
1098
|
// byte-identical to the pre-consult behavior.
|
|
1034
|
-
...(evalMode ||
|
|
1099
|
+
...(evalMode ||
|
|
1100
|
+
env.YAGNI_DISABLE_PREFIX_CONSULT === "1" ||
|
|
1101
|
+
env.YAGNI_DISABLE_PREFIX_CONSULT === "true"
|
|
1035
1102
|
? {}
|
|
1036
1103
|
: {
|
|
1037
1104
|
prefixConsult: {
|
|
@@ -1097,7 +1164,13 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1097
1164
|
body: JSON.stringify(body),
|
|
1098
1165
|
}, {
|
|
1099
1166
|
fetchImpl: authedFetch,
|
|
1100
|
-
policy: {
|
|
1167
|
+
policy: {
|
|
1168
|
+
maxAttempts: 1,
|
|
1169
|
+
backoffBaseMs: 0,
|
|
1170
|
+
backoffMaxMs: 0,
|
|
1171
|
+
timeoutMs: GUARDIAN_EVENT_TIMEOUT_MS,
|
|
1172
|
+
jitterRatio: 0,
|
|
1173
|
+
},
|
|
1101
1174
|
});
|
|
1102
1175
|
if (!res.ok) {
|
|
1103
1176
|
guardianLogSink({ event: "guardian_error_post_failed", status: res.status });
|
|
@@ -1132,25 +1205,27 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1132
1205
|
// registered in eval mode: the gate hides the save option when the dep
|
|
1133
1206
|
// is absent, so a no-op implementation that would still emit
|
|
1134
1207
|
// ask_approved_remembered never exists.
|
|
1135
|
-
...(evalMode
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
allow.
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1208
|
+
...(evalMode
|
|
1209
|
+
? {}
|
|
1210
|
+
: {
|
|
1211
|
+
persistUserRule: (ruleString, cwd) => {
|
|
1212
|
+
mutateLocalConfig(cwd, (config) => {
|
|
1213
|
+
const perms = (config.permissions ?? {});
|
|
1214
|
+
const allow = Array.isArray(perms.allow) ? [...perms.allow] : [];
|
|
1215
|
+
if (!allow.includes(ruleString))
|
|
1216
|
+
allow.push(ruleString);
|
|
1217
|
+
perms.allow = allow;
|
|
1218
|
+
config.permissions = perms;
|
|
1219
|
+
});
|
|
1220
|
+
logEvent({
|
|
1221
|
+
source: "permission-rules",
|
|
1222
|
+
level: "info",
|
|
1223
|
+
event: "rule_saved",
|
|
1224
|
+
sessionId: env.YAGNI_SESSION_ID ?? undefined,
|
|
1225
|
+
fields: { rule: ruleString, destination: "local" },
|
|
1226
|
+
});
|
|
1227
|
+
},
|
|
1228
|
+
}),
|
|
1154
1229
|
// Opt-in storage stream (YAG-510). Tier decides what leaves the machine:
|
|
1155
1230
|
// "off" → nothing (not even sent); "hash" → sha256 + family prefix +
|
|
1156
1231
|
// metadata, no command content; "raw" → adds client-REDACTED command and
|
|
@@ -1197,7 +1272,13 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1197
1272
|
body: JSON.stringify(body),
|
|
1198
1273
|
}, {
|
|
1199
1274
|
fetchImpl: authedFetch,
|
|
1200
|
-
policy: {
|
|
1275
|
+
policy: {
|
|
1276
|
+
maxAttempts: 1,
|
|
1277
|
+
backoffBaseMs: 0,
|
|
1278
|
+
backoffMaxMs: 0,
|
|
1279
|
+
timeoutMs: GUARDIAN_EVENT_TIMEOUT_MS,
|
|
1280
|
+
jitterRatio: 0,
|
|
1281
|
+
},
|
|
1201
1282
|
});
|
|
1202
1283
|
if (!res.ok) {
|
|
1203
1284
|
guardianLogSink({ event: "guardian_event_post_failed", status: res.status });
|
|
@@ -1250,13 +1331,26 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1250
1331
|
},
|
|
1251
1332
|
fetchHeadroom: async (signal) => {
|
|
1252
1333
|
try {
|
|
1253
|
-
const res = await resilientFetch(`${baseUrl}/api/yagni-code/credits`, { method: "GET", headers: { authorization: `Bearer ${getTokenFn() ?? ""}` } }, {
|
|
1334
|
+
const res = await resilientFetch(`${baseUrl}/api/yagni-code/credits`, { method: "GET", headers: { authorization: `Bearer ${getTokenFn() ?? ""}` } }, {
|
|
1335
|
+
fetchImpl: authedFetch,
|
|
1336
|
+
signal,
|
|
1337
|
+
policy: {
|
|
1338
|
+
maxAttempts: 1,
|
|
1339
|
+
backoffBaseMs: 0,
|
|
1340
|
+
backoffMaxMs: 0,
|
|
1341
|
+
timeoutMs: COST_FETCH_TIMEOUT_MS,
|
|
1342
|
+
jitterRatio: 0,
|
|
1343
|
+
},
|
|
1344
|
+
});
|
|
1254
1345
|
if (!res.ok)
|
|
1255
1346
|
return null;
|
|
1256
1347
|
const data = (await res.json());
|
|
1257
1348
|
if (typeof data.remaining !== "number")
|
|
1258
1349
|
return null;
|
|
1259
|
-
return {
|
|
1350
|
+
return {
|
|
1351
|
+
remaining: data.remaining,
|
|
1352
|
+
unit: typeof data.unit === "string" ? data.unit : "credits",
|
|
1353
|
+
};
|
|
1260
1354
|
}
|
|
1261
1355
|
catch {
|
|
1262
1356
|
return null;
|
|
@@ -1268,7 +1362,17 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1268
1362
|
fetchSpend: yagniSessionId
|
|
1269
1363
|
? async (signal) => {
|
|
1270
1364
|
try {
|
|
1271
|
-
const res = await resilientFetch(buildSpendUrl(baseUrl, yagniSessionId, sessionRunIds()), { method: "GET", headers: { authorization: `Bearer ${getTokenFn() ?? ""}` } }, {
|
|
1365
|
+
const res = await resilientFetch(buildSpendUrl(baseUrl, yagniSessionId, sessionRunIds()), { method: "GET", headers: { authorization: `Bearer ${getTokenFn() ?? ""}` } }, {
|
|
1366
|
+
fetchImpl: authedFetch,
|
|
1367
|
+
signal,
|
|
1368
|
+
policy: {
|
|
1369
|
+
maxAttempts: 1,
|
|
1370
|
+
backoffBaseMs: 0,
|
|
1371
|
+
backoffMaxMs: 0,
|
|
1372
|
+
timeoutMs: COST_FETCH_TIMEOUT_MS,
|
|
1373
|
+
jitterRatio: 0,
|
|
1374
|
+
},
|
|
1375
|
+
});
|
|
1272
1376
|
if (!res.ok)
|
|
1273
1377
|
return null;
|
|
1274
1378
|
return parseSpendResponse(await res.json());
|
|
@@ -1571,6 +1675,34 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1571
1675
|
// the sandbox to reach bash, so the hatch is the one carve-out.
|
|
1572
1676
|
sandboxHandle.registerOwnBash();
|
|
1573
1677
|
}
|
|
1678
|
+
else if (toolchainHook) {
|
|
1679
|
+
// Nothing above takes bash over (eval mode, the classic-rows hatch), so
|
|
1680
|
+
// the toolchain reaches the model's spawns through pi's own bash
|
|
1681
|
+
// definition: its schema and rendering unchanged, its PATH the file's.
|
|
1682
|
+
// Fail-soft like the condensed registration: pi's stock bash remains, and
|
|
1683
|
+
// the trail says which step threw (building the definition from the
|
|
1684
|
+
// user's shell settings, or pi's registry taking it) and why.
|
|
1685
|
+
let step = "definition";
|
|
1686
|
+
try {
|
|
1687
|
+
const bash = toolchainBashToolDefinition(toolchainHook, process.cwd());
|
|
1688
|
+
step = "register";
|
|
1689
|
+
pi.registerTool(bash);
|
|
1690
|
+
}
|
|
1691
|
+
catch (err) {
|
|
1692
|
+
logEvent({
|
|
1693
|
+
source: "toolchain",
|
|
1694
|
+
level: "warn",
|
|
1695
|
+
event: "bash_register_failed",
|
|
1696
|
+
fields: {
|
|
1697
|
+
step,
|
|
1698
|
+
errorClass: err instanceof Error ? err.constructor.name : typeof err,
|
|
1699
|
+
// Scrubbed, then cut. A thrown value that is no Error gives no text:
|
|
1700
|
+
// coercing one can itself throw (a null-prototype object).
|
|
1701
|
+
message: err instanceof Error ? scrubSecrets(err.message).slice(0, 300) : "",
|
|
1702
|
+
},
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1574
1706
|
// Own the identity + inject live company context (and repo rules) on every
|
|
1575
1707
|
// turn. The extension loads identically in every pi process this app spawns —
|
|
1576
1708
|
// the interactive driver AND every `/go` stage child, subagent, and advisor
|
|
@@ -1584,15 +1716,8 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1584
1716
|
// Resolved per turn (not once at activation): /ultra can flip the driver
|
|
1585
1717
|
// between the delegation and diamond identities mid-session.
|
|
1586
1718
|
const isDriver = isDriverCaller(env);
|
|
1587
|
-
//
|
|
1588
|
-
//
|
|
1589
|
-
// harness's toolchain file, and eval mode, which is where team_read (the
|
|
1590
|
-
// tool the section sends GitHub reads to) is registered. A local eval
|
|
1591
|
-
// compare sets eval mode without the harness, so it keeps the laptop's.
|
|
1592
|
-
const sandboxSession = evalMode && runsInSandboxHarness(env);
|
|
1593
|
-
// A stage child of `yagni go --headless --cloud`: headlessGo sets the env
|
|
1594
|
-
// where it parses the flag, and the session is told the cloud sandbox.
|
|
1595
|
-
const cloudSession = sandboxSession && env[CLOUD_SESSION_ENV] === "1";
|
|
1719
|
+
// `sandboxSession` and `cloudSession` (above, beside eval mode) pick the
|
|
1720
|
+
// "Your sandbox" section in place of the laptop's.
|
|
1596
1721
|
pi.on("before_agent_start", (event) => noBranding
|
|
1597
1722
|
? undefined
|
|
1598
1723
|
: {
|
|
@@ -1629,7 +1754,11 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1629
1754
|
const sessionIdForLog = () => deps.env?.YAGNI_SESSION_ID;
|
|
1630
1755
|
pi.on("turn_start", (event) => {
|
|
1631
1756
|
turnStartMs.set(event.turnIndex, Date.now());
|
|
1632
|
-
logTurnLifecycle({
|
|
1757
|
+
logTurnLifecycle({
|
|
1758
|
+
kind: "turn_start",
|
|
1759
|
+
sessionId: sessionIdForLog(),
|
|
1760
|
+
turnIndex: event.turnIndex,
|
|
1761
|
+
});
|
|
1633
1762
|
});
|
|
1634
1763
|
pi.on("turn_end", (event) => {
|
|
1635
1764
|
const startedAt = turnStartMs.get(event.turnIndex);
|
|
@@ -1683,8 +1812,8 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1683
1812
|
// only place to detect it and trigger recovery. The regex matches
|
|
1684
1813
|
// "yagni login" (the backend's auth-error message) but NOT
|
|
1685
1814
|
// "request_too_large" (YAG-460's overflow marker).
|
|
1686
|
-
const isAuthError = /yagni login/i.test(msg.errorMessage)
|
|
1687
|
-
|
|
1815
|
+
const isAuthError = /yagni login/i.test(msg.errorMessage) &&
|
|
1816
|
+
!/request_too_large|context_too_large/i.test(msg.errorMessage);
|
|
1688
1817
|
if (isAuthError) {
|
|
1689
1818
|
lastAuthRecovery = null;
|
|
1690
1819
|
let rotated = false;
|
|
@@ -1738,9 +1867,9 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1738
1867
|
const isContextTooLarge = /request_too_large|context_too_large/i.test(msg.errorMessage);
|
|
1739
1868
|
if (!isContextTooLarge)
|
|
1740
1869
|
return;
|
|
1741
|
-
const explanation = "The conversation context is too large for the model (request_too_large). "
|
|
1742
|
-
|
|
1743
|
-
|
|
1870
|
+
const explanation = "The conversation context is too large for the model (request_too_large). " +
|
|
1871
|
+
"YAGNI Code is compacting the session and retrying automatically — " +
|
|
1872
|
+
"if it does not recover, run /compact or start a new session.";
|
|
1744
1873
|
if (ctx.hasUI) {
|
|
1745
1874
|
try {
|
|
1746
1875
|
ctx.ui.notify(explanation, "warning");
|
|
@@ -1997,7 +2126,7 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
1997
2126
|
// not (an allowed command failing the same way ten times is denied).
|
|
1998
2127
|
if (command) {
|
|
1999
2128
|
permissionGate.recordBashOutcome(command, event.isError === true, (event.content ?? [])
|
|
2000
|
-
.map((c) =>
|
|
2129
|
+
.map((c) => c && typeof c === "object" && "text" in c ? String(c.text) : "")
|
|
2001
2130
|
.join("\n"));
|
|
2002
2131
|
}
|
|
2003
2132
|
// The auto-sandbox rung's prior-failure evidence: a SANDBOXED bash
|
|
@@ -2006,12 +2135,9 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
2006
2135
|
// auto-sandboxing into the same denial forever. Only sandboxed runs
|
|
2007
2136
|
// count (an escaped run's failure text proves nothing about the
|
|
2008
2137
|
// sandbox); only the trimmed command is kept, never logged.
|
|
2009
|
-
if (sandboxHandle &&
|
|
2010
|
-
!input.dangerouslyDisableSandbox &&
|
|
2011
|
-
event.isError &&
|
|
2012
|
-
command) {
|
|
2138
|
+
if (sandboxHandle && !input.dangerouslyDisableSandbox && event.isError && command) {
|
|
2013
2139
|
const text = (event.content ?? [])
|
|
2014
|
-
.map((c) =>
|
|
2140
|
+
.map((c) => c && typeof c === "object" && "text" in c ? String(c.text) : "")
|
|
2015
2141
|
.join("\n");
|
|
2016
2142
|
if (isSandboxDenialOutput(text)) {
|
|
2017
2143
|
recordSandboxedFailure(command);
|
|
@@ -2064,9 +2190,7 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
2064
2190
|
// (a tool's own failure text). Keep the always-on trail content-free: use
|
|
2065
2191
|
// ONLY the Error subclass name, never the message or a String() of the
|
|
2066
2192
|
// result's error payload.
|
|
2067
|
-
const errorClass = event.result instanceof Error
|
|
2068
|
-
? event.result.name || "Error"
|
|
2069
|
-
: "tool_error";
|
|
2193
|
+
const errorClass = event.result instanceof Error ? event.result.name || "Error" : "tool_error";
|
|
2070
2194
|
logEvent({
|
|
2071
2195
|
source: "tool",
|
|
2072
2196
|
level: "error",
|
|
@@ -2103,8 +2227,8 @@ export { recordDecision } from "./recordDecisionTool.js";
|
|
|
2103
2227
|
// free text first-class) + the explicit /setup-team drafting flow.
|
|
2104
2228
|
export { runInitPass, runTeamSetup, registerTeamSetupCommand, isFreshWorkspace, readRepoIntake, draftEngineering, summarizeDraft, FRESH_BRIEF_MIN_CHARS, } from "./initPass.js";
|
|
2105
2229
|
// Onramp Door B (F2a): the one-time init-pass idempotency marker.
|
|
2106
|
-
export { isInitDone, markInitDone, initDoneMarkerFile, _setInitDoneHomeForTest } from "./initDone.js";
|
|
2107
|
-
export { brandSystemPrompt, YAGNI_IDENTITY, YAGNI_IDENTITY_DRIVER, YAGNI_IDENTITY_ULTRA, BRAND_NAME } from "./branding.js";
|
|
2230
|
+
export { isInitDone, markInitDone, initDoneMarkerFile, _setInitDoneHomeForTest, } from "./initDone.js";
|
|
2231
|
+
export { brandSystemPrompt, YAGNI_IDENTITY, YAGNI_IDENTITY_DRIVER, YAGNI_IDENTITY_ULTRA, BRAND_NAME, } from "./branding.js";
|
|
2108
2232
|
// YAG-506: user-configurable lifecycle hooks.
|
|
2109
2233
|
export { loadHooksConfig, makeHookRunner, registerHooks, matchesMatcher, parsePreToolUseOutput, parsePermissionRequestOutput, parseAdditionalContext, parseCompactCancel, HOOK_SUPPORTED_EVENTS, } from "./hooks.js";
|
|
2110
2234
|
// Ultra mode (/ultra): the aggressive fan-out/verify/synthesize dial.
|
|
@@ -2118,7 +2242,7 @@ export { runPipeline } from "./pipeline/orchestrator.js";
|
|
|
2118
2242
|
export { HEADLESS_GO_EXIT, HEADLESS_GO_USAGE, parseHeadlessGoArgs, runHeadlessGo, validateHeadlessGoArgs, } from "./pipeline/headlessGo.js";
|
|
2119
2243
|
// Mission mode: the pure rules for entering the pipeline on an already-approved
|
|
2120
2244
|
// plan (map/plan skipped, memo as the stand-in repo brief, FINISH not ours).
|
|
2121
|
-
export { isMissionMode, missionSeed, missionSkippedStages, normalizeMission } from "./pipeline/mission.js";
|
|
2245
|
+
export { isMissionMode, missionSeed, missionSkippedStages, normalizeMission, } from "./pipeline/mission.js";
|
|
2122
2246
|
// The eval/smoke tier ceiling (YAGNI_GO_TIER_CAP), clamped centrally in runStage.
|
|
2123
2247
|
export { clampTier, parseTierCap, resolveTierCap, TIER_CAP_ENV } from "./pipeline/tierCap.js";
|
|
2124
2248
|
// R1: the in-loop resilience HOF a dev can compose with (or replace at) the
|
|
@@ -2126,9 +2250,9 @@ export { clampTier, parseTierCap, resolveTierCap, TIER_CAP_ENV } from "./pipelin
|
|
|
2126
2250
|
export { withResilience, classifyTransient, composeAbortSignal } from "./pipeline/resilience.js";
|
|
2127
2251
|
export { DEFAULT_RESILIENCE_POLICY } from "./pipeline/types.js";
|
|
2128
2252
|
// P4: the deterministic verify gate seam (override the command via .yagni-code/verify).
|
|
2129
|
-
export { buildVerifyEnv, detectVerifyCommand, makeRunVerify, parseVerifyFailures } from "./pipeline/verify.js";
|
|
2253
|
+
export { buildVerifyEnv, detectVerifyCommand, makeRunVerify, parseVerifyFailures, } from "./pipeline/verify.js";
|
|
2130
2254
|
// M6: the report-only grounded-vs-blind eval engine.
|
|
2131
|
-
export { blindStages, reportOnlyStages, makeGroundedVsBlindEval, formatComparisonReport } from "./pipeline/eval.js";
|
|
2255
|
+
export { blindStages, reportOnlyStages, makeGroundedVsBlindEval, formatComparisonReport, } from "./pipeline/eval.js";
|
|
2132
2256
|
export { registerGoCompareCommand } from "./pipeline/goCompareCommand.js";
|
|
2133
2257
|
// The general subagent tool: Claude Code-format agent discovery + fan-out.
|
|
2134
2258
|
export { registerSubagents, makeSubagentTool, discoverSubagents, parseAgentMarkdown, buildSubagentStage, formatAgentList, mapModelTier, SUBAGENT_TOOL_NAME, GENERAL_AGENT_NAME, MAX_PARALLEL_SUBAGENTS, MAX_PARALLEL_SUBAGENTS_ULTRA, DEFAULT_SUBAGENT_TOOLS, } from "./subagents.js";
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* @yagni/shared, so it reads the same `{ version: 1, path, env, tools }` with
|
|
19
19
|
* its own parser. A change there is a change here.
|
|
20
20
|
*/
|
|
21
|
-
import { type BashOperations, type BashSpawnHook } from "@earendil-works/pi-coding-agent";
|
|
21
|
+
import { createBashToolDefinition, type BashOperations, type BashSpawnHook } from "@earendil-works/pi-coding-agent";
|
|
22
22
|
import { type SinkEvent } from "../errorSink.js";
|
|
23
23
|
export interface ToolchainFile {
|
|
24
24
|
version: 1;
|
|
@@ -51,8 +51,8 @@ export declare function composeSpawnHooks(...hooks: Array<BashSpawnHook | undefi
|
|
|
51
51
|
/**
|
|
52
52
|
* This process runs under the sandbox harness (`infra/e2b/pi/pi-harness.sh`
|
|
53
53
|
* exports `YAGNI_TOOLCHAIN_FILE` on every E2B and Machines session, Reeve's
|
|
54
|
-
* and Wright's alike,
|
|
55
|
-
* terminal never sets it.
|
|
54
|
+
* and Wright's alike, the one-attempt lane's launch line does too, and `/go`
|
|
55
|
+
* children inherit it). A developer's own terminal never sets it.
|
|
56
56
|
*/
|
|
57
57
|
export declare function runsInSandboxHarness(env?: NodeJS.ProcessEnv): boolean;
|
|
58
58
|
/**
|
|
@@ -61,6 +61,16 @@ export declare function runsInSandboxHarness(env?: NodeJS.ProcessEnv): boolean;
|
|
|
61
61
|
* unreadable logs once.
|
|
62
62
|
*/
|
|
63
63
|
export declare function toolchainSpawnHookFromEnv(env?: NodeJS.ProcessEnv, log?: Log): BashSpawnHook | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* pi's own bash tool as its session builds it (the user's shell and command
|
|
66
|
+
* prefix), with the hook on every spawn. For a session where no other
|
|
67
|
+
* registration takes bash over (eval mode, the classic-rows hatch): its name,
|
|
68
|
+
* description, parameters and rendering stay pi's, and only the spawn's PATH
|
|
69
|
+
* and variables change. The shell settings are the user's own, never the
|
|
70
|
+
* repository's: pi's session reads a repository's settings only once the
|
|
71
|
+
* folder is trusted, and nothing in a sandbox trusts one.
|
|
72
|
+
*/
|
|
73
|
+
export declare function toolchainBashToolDefinition(hook: BashSpawnHook, cwd: string): ReturnType<typeof createBashToolDefinition>;
|
|
64
74
|
/**
|
|
65
75
|
* `user_bash` (`!` commands) through pi's own local backend, with the hook's
|
|
66
76
|
* environment: the same toolchain the model's bash tool gets.
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* its own parser. A change there is a change here.
|
|
20
20
|
*/
|
|
21
21
|
import { readFileSync } from "node:fs";
|
|
22
|
-
import { createLocalBashOperations, SettingsManager, } from "@earendil-works/pi-coding-agent";
|
|
22
|
+
import { createBashToolDefinition, createLocalBashOperations, getAgentDir, SettingsManager, } from "@earendil-works/pi-coding-agent";
|
|
23
23
|
import { logEvent } from "../errorSink.js";
|
|
24
24
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25
25
|
/** The file, or null when the text is not a well-formed version 1 file. */
|
|
@@ -70,12 +70,22 @@ export function readToolchainFile(path, log = logEvent) {
|
|
|
70
70
|
}
|
|
71
71
|
catch (err) {
|
|
72
72
|
const code = isRecord(err) && typeof err.code === "string" ? err.code : "read_failed";
|
|
73
|
-
log({
|
|
73
|
+
log({
|
|
74
|
+
source: "toolchain",
|
|
75
|
+
level: "warn",
|
|
76
|
+
event: "file_unreadable",
|
|
77
|
+
fields: { path, reason: code },
|
|
78
|
+
});
|
|
74
79
|
return null;
|
|
75
80
|
}
|
|
76
81
|
const file = parseToolchainFile(text);
|
|
77
82
|
if (!file)
|
|
78
|
-
log({
|
|
83
|
+
log({
|
|
84
|
+
source: "toolchain",
|
|
85
|
+
level: "warn",
|
|
86
|
+
event: "file_unreadable",
|
|
87
|
+
fields: { path, reason: "malformed" },
|
|
88
|
+
});
|
|
79
89
|
return file;
|
|
80
90
|
}
|
|
81
91
|
/**
|
|
@@ -100,8 +110,8 @@ export function composeSpawnHooks(...hooks) {
|
|
|
100
110
|
/**
|
|
101
111
|
* This process runs under the sandbox harness (`infra/e2b/pi/pi-harness.sh`
|
|
102
112
|
* exports `YAGNI_TOOLCHAIN_FILE` on every E2B and Machines session, Reeve's
|
|
103
|
-
* and Wright's alike,
|
|
104
|
-
* terminal never sets it.
|
|
113
|
+
* and Wright's alike, the one-attempt lane's launch line does too, and `/go`
|
|
114
|
+
* children inherit it). A developer's own terminal never sets it.
|
|
105
115
|
*/
|
|
106
116
|
export function runsInSandboxHarness(env = process.env) {
|
|
107
117
|
return Boolean(env.YAGNI_TOOLCHAIN_FILE?.trim());
|
|
@@ -118,6 +128,23 @@ export function toolchainSpawnHookFromEnv(env = process.env, log = logEvent) {
|
|
|
118
128
|
const file = readToolchainFile(path, log);
|
|
119
129
|
return file ? makeToolchainSpawnHook(file) : undefined;
|
|
120
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* pi's own bash tool as its session builds it (the user's shell and command
|
|
133
|
+
* prefix), with the hook on every spawn. For a session where no other
|
|
134
|
+
* registration takes bash over (eval mode, the classic-rows hatch): its name,
|
|
135
|
+
* description, parameters and rendering stay pi's, and only the spawn's PATH
|
|
136
|
+
* and variables change. The shell settings are the user's own, never the
|
|
137
|
+
* repository's: pi's session reads a repository's settings only once the
|
|
138
|
+
* folder is trusted, and nothing in a sandbox trusts one.
|
|
139
|
+
*/
|
|
140
|
+
export function toolchainBashToolDefinition(hook, cwd) {
|
|
141
|
+
const settings = SettingsManager.create(cwd, getAgentDir(), { projectTrusted: false });
|
|
142
|
+
return createBashToolDefinition(cwd, {
|
|
143
|
+
shellPath: settings.getShellPath(),
|
|
144
|
+
commandPrefix: settings.getShellCommandPrefix(),
|
|
145
|
+
spawnHook: hook,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
121
148
|
/**
|
|
122
149
|
* `user_bash` (`!` commands) through pi's own local backend, with the hook's
|
|
123
150
|
* environment: the same toolchain the model's bash tool gets.
|
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.2020.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": "e3bb7574f09420b1c1a9a25a971db7ae109f468f"
|
|
62
62
|
}
|