@yagni-app/code 0.2.1 → 0.3.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.
Files changed (98) hide show
  1. package/dist/cli.d.ts +30 -0
  2. package/dist/cli.js +147 -3
  3. package/dist/connectClaudeCode.d.ts +77 -0
  4. package/dist/connectClaudeCode.js +228 -0
  5. package/dist/connectCodex.d.ts +75 -0
  6. package/dist/connectCodex.js +201 -0
  7. package/dist/doctor.d.ts +1 -1
  8. package/dist/doctor.js +1 -1
  9. package/dist/extension/advisor.d.ts +4 -4
  10. package/dist/extension/advisor.js +6 -7
  11. package/dist/extension/approvedPrefixes.d.ts +103 -0
  12. package/dist/extension/approvedPrefixes.js +282 -0
  13. package/dist/extension/askAdvisorTool.d.ts +20 -5
  14. package/dist/extension/askAdvisorTool.js +126 -20
  15. package/dist/extension/askYagniTool.d.ts +23 -0
  16. package/dist/extension/askYagniTool.js +91 -2
  17. package/dist/extension/branding.d.ts +34 -3
  18. package/dist/extension/branding.js +114 -13
  19. package/dist/extension/chipEditor.d.ts +30 -9
  20. package/dist/extension/chipEditor.js +173 -59
  21. package/dist/extension/claudeRules.d.ts +0 -2
  22. package/dist/extension/claudeRules.js +0 -8
  23. package/dist/extension/cmux/dispatcher.d.ts +25 -0
  24. package/dist/extension/cmux/dispatcher.js +266 -0
  25. package/dist/extension/cmux/hooks.d.ts +12 -0
  26. package/dist/extension/cmux/hooks.js +192 -0
  27. package/dist/extension/cmux/index.d.ts +3 -0
  28. package/dist/extension/cmux/index.js +155 -0
  29. package/dist/extension/cmux/naming.d.ts +5 -0
  30. package/dist/extension/cmux/naming.js +23 -0
  31. package/dist/extension/cmux/state.d.ts +33 -0
  32. package/dist/extension/cmux/state.js +142 -0
  33. package/dist/extension/config.d.ts +44 -1
  34. package/dist/extension/config.js +37 -4
  35. package/dist/extension/costHud.d.ts +16 -22
  36. package/dist/extension/costHud.js +8 -47
  37. package/dist/extension/crashReport.js +1 -3
  38. package/dist/extension/execPolicy.d.ts +135 -0
  39. package/dist/extension/execPolicy.js +936 -0
  40. package/dist/extension/flywheel.d.ts +44 -0
  41. package/dist/extension/flywheel.js +53 -0
  42. package/dist/extension/footer.d.ts +118 -0
  43. package/dist/extension/footer.js +308 -0
  44. package/dist/extension/guardian.d.ts +139 -0
  45. package/dist/extension/guardian.js +237 -0
  46. package/dist/extension/index.d.ts +35 -7
  47. package/dist/extension/index.js +338 -33
  48. package/dist/extension/mineBeat.d.ts +95 -0
  49. package/dist/extension/mineBeat.js +193 -0
  50. package/dist/extension/permission.d.ts +124 -10
  51. package/dist/extension/permission.js +603 -52
  52. package/dist/extension/pipeline/childRegistry.d.ts +41 -0
  53. package/dist/extension/pipeline/childRegistry.js +118 -0
  54. package/dist/extension/pipeline/finish.js +5 -1
  55. package/dist/extension/pipeline/goCommand.d.ts +1 -1
  56. package/dist/extension/pipeline/goCommand.js +40 -9
  57. package/dist/extension/pipeline/goStatusCommands.d.ts +10 -0
  58. package/dist/extension/pipeline/goStatusCommands.js +61 -1
  59. package/dist/extension/pipeline/personas.js +26 -1
  60. package/dist/extension/pipeline/resilience.d.ts +2 -1
  61. package/dist/extension/pipeline/resilience.js +21 -2
  62. package/dist/extension/pipeline/runRegistry.d.ts +23 -1
  63. package/dist/extension/pipeline/runRegistry.js +57 -1
  64. package/dist/extension/pipeline/runner.js +4 -0
  65. package/dist/extension/pipeline/verify.d.ts +4 -0
  66. package/dist/extension/pipeline/verify.js +48 -26
  67. package/dist/extension/recordDecisionTool.d.ts +8 -0
  68. package/dist/extension/recordDecisionTool.js +24 -0
  69. package/dist/extension/redact.d.ts +20 -0
  70. package/dist/extension/redact.js +64 -0
  71. package/dist/extension/rerouteNotice.d.ts +3 -4
  72. package/dist/extension/rerouteNotice.js +20 -11
  73. package/dist/extension/subagentRender.d.ts +129 -0
  74. package/dist/extension/subagentRender.js +441 -0
  75. package/dist/extension/subagents.d.ts +11 -8
  76. package/dist/extension/subagents.js +162 -37
  77. package/dist/extension/ticketTools.d.ts +37 -0
  78. package/dist/extension/ticketTools.js +117 -0
  79. package/dist/extension/todos.d.ts +28 -1
  80. package/dist/extension/todos.js +76 -1
  81. package/dist/extension/tokenProvider.js +46 -5
  82. package/dist/extension/ultra.d.ts +27 -0
  83. package/dist/extension/ultra.js +76 -0
  84. package/dist/launch.d.ts +7 -0
  85. package/dist/launch.js +24 -12
  86. package/dist/login.d.ts +4 -2
  87. package/dist/login.js +19 -4
  88. package/dist/padding.d.ts +22 -0
  89. package/dist/padding.js +25 -0
  90. package/dist/promptEnrichment.d.ts +40 -0
  91. package/dist/promptEnrichment.js +85 -0
  92. package/dist/signalForward.d.ts +60 -0
  93. package/dist/signalForward.js +130 -0
  94. package/dist/token.d.ts +25 -0
  95. package/dist/token.js +45 -0
  96. package/package.json +6 -5
  97. package/dist/extension/boostCommand.d.ts +0 -144
  98. package/dist/extension/boostCommand.js +0 -263
@@ -0,0 +1,237 @@
1
+ /**
2
+ * The Guardian — LLM auto-review of prompt-band bash commands (YAG-504).
3
+ *
4
+ * Pure half: verdict types, state tracking, circuit breaker, JSON parsing.
5
+ * I/O half: reviewCommand spawns a locked-down child pi (same runStage seam
6
+ * the advisor uses) on the efficient tier with read-only tools and a risk
7
+ * policy persona. The child returns a JSON verdict; the gate acts on it.
8
+ *
9
+ * Same pure/IO split as advisor.ts (decideConsult pure, askAdvisorTool I/O)
10
+ * and permission.ts (decideGate pure, registerPermissionGate I/O), for the
11
+ * same reason: the rules are what need exhaustive tests, and they must not
12
+ * require a child process to exercise.
13
+ *
14
+ * Trigger: the exec policy classifies a bash command as "prompt" (not clearly
15
+ * safe, not clearly forbidden). The Guardian reviews it instead of interrupting
16
+ * the user. On allow, the command runs. On ask (YAG-510), the user arbitrates:
17
+ * the gate shows the Guardian's question-rationale and the user approves or
18
+ * declines. On deny, the agent sees the rationale and is told to find a safer
19
+ * alternative or ask the user. On timeout/error, auto mode falls back to an
20
+ * ask when a UI exists, else fails closed; review mode falls back to the
21
+ * ordinary user confirm.
22
+ *
23
+ * Circuit breaker: 3 consecutive denials within one user prompt → escalation
24
+ * (ask the user once) or, headless, interruption. Denial streaks reset on
25
+ * `before_agent_start`, which fires once per USER PROMPT (not per LLM turn).
26
+ * An `ask` outcome leaves the streak untouched — neither a denial nor an
27
+ * exoneration — so an ask-preferring model cannot disarm the breaker by
28
+ * alternating deny/ask.
29
+ */
30
+ import { runStage as defaultRunStage } from "./pipeline/runner.js";
31
+ export const DEFAULT_GUARDIAN_LIMITS = {
32
+ maxReviews: 120,
33
+ maxConsecutiveDenials: 3,
34
+ timeoutMs: 15_000,
35
+ };
36
+ /**
37
+ * Resolve Guardian limits from the environment. `YAGNI_GUARDIAN_MAX_REVIEWS`
38
+ * overrides the sliding-window review cap; anything non-numeric or < 1 falls
39
+ * back to the default (a bad value must never zero out the cap and lock the
40
+ * session).
41
+ */
42
+ export function resolveGuardianLimits(env = process.env) {
43
+ const raw = env.YAGNI_GUARDIAN_MAX_REVIEWS?.trim();
44
+ const parsed = raw ? Number.parseInt(raw, 10) : NaN;
45
+ const maxReviews = Number.isFinite(parsed) && parsed >= 1 ? parsed : DEFAULT_GUARDIAN_LIMITS.maxReviews;
46
+ return { ...DEFAULT_GUARDIAN_LIMITS, maxReviews };
47
+ }
48
+ /** The model tier the Guardian runs on. Configurable via YAGNI_GUARDIAN_TIER. */
49
+ export const GUARDIAN_MODEL_TIER = "efficient";
50
+ /** Read-only tools — the Guardian can read files for context but cannot write or execute. */
51
+ export const GUARDIAN_TOOLS = ["read"];
52
+ // --- State ---
53
+ /**
54
+ * The review-cap window. `reviews` counts consults inside a SLIDING window
55
+ * rather than for the session's lifetime: a 24/7 session (a fleet operator's
56
+ * always-on terminal) must regain review capacity as old consults age out,
57
+ * not hard-block forever after the first N. The cap is a cost/runaway bound,
58
+ * not a safety bound — safety is the verdicts themselves.
59
+ */
60
+ export const GUARDIAN_REVIEW_WINDOW_MS = 60 * 60_000;
61
+ export function makeGuardianState(now = Date.now) {
62
+ const reviewTimes = [];
63
+ let consecutiveDenials = 0;
64
+ const prune = () => {
65
+ const cutoff = now() - GUARDIAN_REVIEW_WINDOW_MS;
66
+ while (reviewTimes.length > 0 && reviewTimes[0] <= cutoff)
67
+ reviewTimes.shift();
68
+ };
69
+ const snapshot = () => ({
70
+ reviews: reviewTimes.length,
71
+ consecutiveDenials,
72
+ });
73
+ return {
74
+ read: () => {
75
+ prune();
76
+ return snapshot();
77
+ },
78
+ recordReview(outcome) {
79
+ prune();
80
+ reviewTimes.push(now());
81
+ if (outcome === "deny") {
82
+ consecutiveDenials += 1;
83
+ }
84
+ else if (outcome === "allow") {
85
+ consecutiveDenials = 0;
86
+ }
87
+ // "ask" leaves the denial streak UNCHANGED: it is neither a denial nor
88
+ // an exoneration. If it reset the streak, deny/ask/deny/ask would never
89
+ // trip the breaker (round-2 review blocker).
90
+ return snapshot();
91
+ },
92
+ resetTurn() {
93
+ consecutiveDenials = 0;
94
+ },
95
+ };
96
+ }
97
+ export function checkCircuitBreaker(state, limits) {
98
+ if (state.consecutiveDenials >= limits.maxConsecutiveDenials) {
99
+ return {
100
+ tripped: true,
101
+ reason: `Guardian denied ${state.consecutiveDenials} consecutive actions this turn. Pausing for human review.`,
102
+ };
103
+ }
104
+ return { tripped: false };
105
+ }
106
+ // --- Verdict parsing (fail closed on malformed) ---
107
+ export function parseVerdict(raw) {
108
+ try {
109
+ // Efficient-tier models may wrap JSON in markdown fences despite
110
+ // instructions to output raw JSON. Extract the first {...} block.
111
+ const jsonMatch = raw.match(/\{[\s\S]*\}/);
112
+ const jsonStr = jsonMatch ? jsonMatch[0] : raw;
113
+ const parsed = JSON.parse(jsonStr);
114
+ const outcome = parsed?.outcome;
115
+ if (outcome !== "allow" && outcome !== "ask" && outcome !== "deny")
116
+ return null;
117
+ const riskLevel = parsed.riskLevel;
118
+ const validLevels = ["low", "medium", "high", "critical"];
119
+ return {
120
+ outcome,
121
+ riskLevel: typeof riskLevel === "string" && validLevels.includes(riskLevel)
122
+ ? riskLevel
123
+ : "medium",
124
+ rationale: typeof parsed.rationale === "string" && parsed.rationale.trim().length > 0
125
+ ? parsed.rationale.trim()
126
+ : "No rationale provided.",
127
+ };
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ }
133
+ // --- /cost subtotal ---
134
+ export function formatGuardianSubtotal(state, limits) {
135
+ if (state.reviews === 0)
136
+ return "";
137
+ const plural = state.reviews === 1 ? "review" : "reviews";
138
+ return `Guardian: ${state.reviews} ${plural}.`;
139
+ }
140
+ /**
141
+ * The synthetic stage a Guardian consult runs as. Borrows the `plan` StageId
142
+ * (same pattern as the advisor) so it doesn't ripple into feed/reducers. The
143
+ * agent name selects the guardian persona from PERSONA_BODIES.
144
+ */
145
+ export function guardianStage(modelTier = GUARDIAN_MODEL_TIER) {
146
+ return {
147
+ id: "plan",
148
+ agent: "guardian",
149
+ model: modelTier,
150
+ tools: GUARDIAN_TOOLS,
151
+ taskTemplate: "{ticket}",
152
+ };
153
+ }
154
+ /**
155
+ * Run a Guardian consult: spawn a locked-down child pi with the risk policy
156
+ * persona and the command as the task. Parse the JSON verdict from the output.
157
+ * Returns { verdict, cost } on success, { verdict: null, error, cost } on failure.
158
+ */
159
+ export async function reviewCommand(command, deps) {
160
+ const runStage = deps.runStage ?? defaultRunStage;
161
+ const stage = guardianStage(deps.modelTier);
162
+ // Build the abort signal with the Guardian timeout (injectable — the gate
163
+ // threads guardianLimits.timeoutMs through).
164
+ const timeoutMs = deps.timeoutMs ?? DEFAULT_GUARDIAN_LIMITS.timeoutMs;
165
+ const controller = new AbortController();
166
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
167
+ timer.unref?.();
168
+ // Chain with the caller's signal if present.
169
+ if (deps.signal) {
170
+ if (deps.signal.aborted)
171
+ controller.abort();
172
+ else
173
+ deps.signal.addEventListener("abort", () => controller.abort(), { once: true });
174
+ }
175
+ try {
176
+ const justificationLine = deps.execJustification
177
+ ? `\n\nWhy this command was routed to you (static policy): ${deps.execJustification}`
178
+ : "";
179
+ const result = await runStage(stage, { ticket: `Review this shell command for safety. Output only the JSON verdict.${justificationLine}\n\nCommand:\n${command}` }, {
180
+ cwd: deps.cwd,
181
+ signal: controller.signal,
182
+ callerLabel: "guardian",
183
+ });
184
+ const cost = result.usage?.cost ?? 0;
185
+ const output = result.finalOutput.trim();
186
+ // An aborted child often RESOLVES with empty/partial output instead of
187
+ // throwing (observed live: ESC → 433ms "empty"). Any result produced
188
+ // while the caller's signal is aborted is an abort, not an outage — the
189
+ // gate must not warn or fall back to an ask on it.
190
+ if (deps.signal?.aborted) {
191
+ return { verdict: null, error: "aborted", cost };
192
+ }
193
+ if (!output) {
194
+ return { verdict: null, error: "empty", cost };
195
+ }
196
+ const verdict = parseVerdict(output);
197
+ if (!verdict) {
198
+ return { verdict: null, error: "malformed", cost };
199
+ }
200
+ return { verdict, cost };
201
+ }
202
+ catch (err) {
203
+ // Distinguish the caller aborting (user hit ESC — must NOT be treated as
204
+ // an outage, or the fallback ask would pop a dialog on an aborted turn),
205
+ // our own timeout, and real network/process errors.
206
+ if (deps.signal?.aborted) {
207
+ return { verdict: null, error: "aborted", cost: 0 };
208
+ }
209
+ if (controller.signal.aborted) {
210
+ return { verdict: null, error: "timeout", cost: 0 };
211
+ }
212
+ return { verdict: null, error: "network", cost: 0 };
213
+ }
214
+ finally {
215
+ clearTimeout(timer);
216
+ }
217
+ }
218
+ /**
219
+ * Create a sanitized diagnostic event. Never includes the raw command text
220
+ * (could contain secrets). YAGNI_DEBUG=1 adds rationale and a command hash.
221
+ */
222
+ export function buildDiagnosticEvent(outcome, opts) {
223
+ const ev = {
224
+ event: "guardian_review",
225
+ outcome,
226
+ ...(opts.durationMs !== undefined ? { durationMs: opts.durationMs } : {}),
227
+ ...(opts.tier !== undefined ? { tier: opts.tier } : {}),
228
+ };
229
+ if (opts.debug) {
230
+ if (opts.rationale)
231
+ ev.rationale = opts.rationale;
232
+ if (opts.commandHash)
233
+ ev.commandHash = opts.commandHash;
234
+ }
235
+ return ev;
236
+ }
237
+ //# sourceMappingURL=guardian.js.map
@@ -4,7 +4,8 @@ import { runInitPass as defaultRunInitPass } from "./initPass.js";
4
4
  import { fetchMcpServers as defaultFetchMcpServers } from "./mcpTools.js";
5
5
  import { type FlushOutcome, type SpoolClientOpts } from "./spool.js";
6
6
  import { type TokenProvider } from "./tokenProvider.js";
7
- import { type ContextBrief, type ModelEntry } from "./config.js";
7
+ import { type CatalogResult, type ContextBrief } from "./config.js";
8
+ import { maybeOfferMiningBeat as defaultMaybeOfferMiningBeat, type MineBeatMarkers } from "./mineBeat.js";
8
9
  /**
9
10
  * YAGNI Code extension entry point.
10
11
  *
@@ -31,12 +32,26 @@ export interface RegisterYagniDeps {
31
32
  baseUrl: string;
32
33
  getToken: () => string | undefined;
33
34
  fetchImpl?: typeof fetch;
34
- }) => Promise<ModelEntry[]>;
35
+ }) => Promise<CatalogResult>;
35
36
  fetchContextBrief?: (opts: {
36
37
  baseUrl: string;
37
38
  getToken: () => string | undefined;
38
39
  fetchImpl?: typeof fetch;
40
+ repo?: string;
39
41
  }) => Promise<ContextBrief | null>;
42
+ /**
43
+ * The session repo (`owner/name`), resolved from the origin remote before
44
+ * the boot brief fetch so `/context` can carry `repoDecisionCount` for the
45
+ * mining beat. Injectable (no git in tests); undefined outside a repo.
46
+ */
47
+ resolveRepoFullName?: (cwd: string) => string | undefined;
48
+ /**
49
+ * The opt-in seeding beat itself (Run 7). Injectable so tests assert it
50
+ * fires only on a startup with an explicitly-zero repo ledger.
51
+ */
52
+ offerMiningBeat?: typeof defaultMaybeOfferMiningBeat;
53
+ /** The beat's once-per-repo marker store. Injectable (no disk in tests). */
54
+ mineBeatMarkers?: MineBeatMarkers;
40
55
  /**
41
56
  * Workspace MCP server list seam (YAG-446). The default hits
42
57
  * `/api/yagni-code/mcp/servers` and is fail-soft: older backends and
@@ -63,6 +78,12 @@ export interface RegisterYagniDeps {
63
78
  tokenProvider?: TokenProvider;
64
79
  /** The spool flush (R4 write half), injectable so tests never touch disk. */
65
80
  flushSpool?: (opts: SpoolClientOpts) => Promise<FlushOutcome>;
81
+ /**
82
+ * Non-fatal auth-event reporter (YAG-500 Fix E). Defaults to
83
+ * `makeCrashReporter` gated on `!evalMode`; inject a spy in tests to assert
84
+ * the report is fired with `context: "auth-failure"` and the refresh outcome.
85
+ */
86
+ authReporter?: (error: unknown, context?: string) => Promise<void>;
66
87
  env?: NodeJS.ProcessEnv;
67
88
  }
68
89
  /**
@@ -97,8 +118,11 @@ export declare function parseSpendResponse(data: unknown): SpendResponse | null;
97
118
  export declare function registerYagni(pi: ExtensionAPI, deps?: RegisterYagniDeps): Promise<void>;
98
119
  export default function (pi: ExtensionAPI): Promise<void>;
99
120
  export { makeAskYagniTool } from "./askYagniTool.js";
121
+ export { makeFileTicketTool, makeUpdateTicketStatusTool } from "./ticketTools.js";
100
122
  export { makeAskAdvisorTool, registerAdviseCommand } from "./askAdvisorTool.js";
101
123
  export { ADVISOR_TIER, DEFAULT_ADVISOR_LIMITS, decideConsult, formatAdvisorSubtotal, makeAdvisorState, } from "./advisor.js";
124
+ export { DEFAULT_GUARDIAN_LIMITS, GUARDIAN_MODEL_TIER, formatGuardianSubtotal, makeGuardianState, resolveGuardianLimits, reviewCommand, } from "./guardian.js";
125
+ export type { GuardianOutcome, GuardianVerdict, GuardianState, GuardianStateHandle, GuardianLimits, ReviewResult, ReviewCommandDeps, } from "./guardian.js";
102
126
  export type { Citation, MakeAskYagniToolOptions } from "./askYagniTool.js";
103
127
  export { makeReviewBusinessMatchTool } from "./reviewTool.js";
104
128
  export type { MakeReviewToolOptions } from "./reviewTool.js";
@@ -113,9 +137,11 @@ export type { RecordDecisionParams } from "./recordDecisionTool.js";
113
137
  export { runInitPass, runTeamSetup, registerTeamSetupCommand, isFreshWorkspace, readRepoIntake, draftEngineering, summarizeDraft, FRESH_BRIEF_MIN_CHARS, } from "./initPass.js";
114
138
  export type { RunInitPassDeps, InitPassOutcome, RunTeamSetupDeps, TeamSetupOutcome, RepoIntake, RepoIntakeFs, EngineeringDraft, DraftTeam, } from "./initPass.js";
115
139
  export { isInitDone, markInitDone, initDoneMarkerFile, _setInitDoneHomeForTest } from "./initDone.js";
116
- export { brandSystemPrompt, YAGNI_IDENTITY, YAGNI_IDENTITY_DRIVER, BRAND_NAME } from "./branding.js";
140
+ export { brandSystemPrompt, YAGNI_IDENTITY, YAGNI_IDENTITY_DRIVER, YAGNI_IDENTITY_ULTRA, BRAND_NAME } from "./branding.js";
141
+ export { createUltraHolder, registerUltraCommand } from "./ultra.js";
142
+ export type { UltraHolder } from "./ultra.js";
117
143
  export { attributionHeaders, isDriverCaller, fetchCatalog, getToken, getWorkspaceId, resolveBaseUrl, sanitizeCallerSegment, } from "./config.js";
118
- export type { FetchCatalogOptions, ModelEntry } from "./config.js";
144
+ export type { CatalogResult, FetchCatalogOptions, ModelEntry } from "./config.js";
119
145
  export { buildYagniProvider } from "./provider.js";
120
146
  export { registerGoCommand } from "./pipeline/goCommand.js";
121
147
  export type { RegisterGoDeps } from "./pipeline/goCommand.js";
@@ -130,11 +156,13 @@ export type { VerifyCommand, VerifyOutcome } from "./pipeline/verify.js";
130
156
  export { blindStages, reportOnlyStages, makeGroundedVsBlindEval, formatComparisonReport } from "./pipeline/eval.js";
131
157
  export type { ComparisonReport, LaneFit, LaneOutcome } from "./pipeline/eval.js";
132
158
  export { registerGoCompareCommand } from "./pipeline/goCompareCommand.js";
133
- export { registerSubagents, makeSubagentTool, discoverSubagents, parseAgentMarkdown, buildSubagentStage, formatAgentList, mapModelTier, SUBAGENT_TOOL_NAME, GENERAL_AGENT_NAME, MAX_PARALLEL_SUBAGENTS, DEFAULT_SUBAGENT_TOOLS, } from "./subagents.js";
159
+ 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";
134
160
  export type { SubagentDef, SubagentSource } from "./subagents.js";
135
- export { registerTodos, makeTodoTool, normalizeTodos, reconstructTodos, renderTodoWidget, formatTodoList, todoSummary, TODO_TOOL_NAME, MAX_TODOS, } from "./todos.js";
161
+ export { registerTodos, makeTodoTool, normalizeTodos, reconstructTodos, renderTodoWidget, formatTodoList, formatTodoReminder, shouldRemindTodos, todoSummary, TODO_TOOL_NAME, TODO_REMINDER_TURNS, MAX_TODOS, } from "./todos.js";
136
162
  export type { TodoItem, TodoStatus, TodoTheme } from "./todos.js";
137
- export { decideGate, registerPermissionGate, filterStalePlanContext, DEFAULT_PERMISSION_POLICY, PLAN_CONTEXT_TYPE, PLAN_CONTEXT_MESSAGE, } from "./permission.js";
163
+ export { decideGate, registerPermissionGate, filterStaleModeContext, filterStalePlanContext, buildModeContextMessage, DEFAULT_PERMISSION_POLICY, MODE_CONTEXT_TYPE, PLAN_CONTEXT_TYPE, PLAN_CONTEXT_MESSAGE, } from "./permission.js";
164
+ export { classifyCommand, DEFAULT_EXEC_POLICY, } from "./execPolicy.js";
165
+ export type { ExecDecision, ExecPolicy, PrefixRule, ExecClassification, } from "./execPolicy.js";
138
166
  export type { PermissionMode, PermissionPolicy, GateDecision, RegisterPermissionDeps, BlessRememberInfo, } from "./permission.js";
139
167
  export { makeBlessStore, blessPath } from "./bless.js";
140
168
  export type { BlessStore, BlessRule } from "./bless.js";