indusagi-coding-agent 0.1.62 → 0.2.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/entry.js +5395 -1509
- package/dist/guardrails.js +2031 -0
- package/dist/index.js +18371 -0
- package/dist/types/boot/contract.d.ts +2 -0
- package/dist/types/boot/index.d.ts +1 -1
- package/dist/types/boot/runners/addon-wiring.d.ts +103 -0
- package/dist/types/boot/runners/addon-wiring.test.d.ts +19 -0
- package/dist/types/boot/runners/checkpoint.d.ts +133 -0
- package/dist/types/boot/runners/checkpoint.test.d.ts +12 -0
- package/dist/types/boot/runners/delegate-runner.d.ts +83 -0
- package/dist/types/boot/runners/delegate-runner.test.d.ts +13 -0
- package/dist/types/boot/runners/index.d.ts +2 -0
- package/dist/types/boot/runners/memdir.d.ts +103 -0
- package/dist/types/boot/runners/memdir.test.d.ts +12 -0
- package/dist/types/boot/runners/read-state.d.ts +82 -0
- package/dist/types/boot/runners/read-state.test.d.ts +10 -0
- package/dist/types/boot/runners/session.d.ts +37 -2
- package/dist/types/boot/runners/session.test.d.ts +10 -0
- package/dist/types/briefing/context-docs.d.ts +38 -0
- package/dist/types/briefing/context-docs.test.d.ts +18 -0
- package/dist/types/briefing/index.d.ts +2 -0
- package/dist/types/capability-deck/cards/index.d.ts +6 -0
- package/dist/types/capability-deck/cards/memory-card.d.ts +9 -10
- package/dist/types/capability-deck/cards/plan-file.d.ts +56 -0
- package/dist/types/capability-deck/cards/plan-tools.d.ts +97 -0
- package/dist/types/capability-deck/cards/plan-tools.test.d.ts +9 -0
- package/dist/types/capability-deck/checkpoint.int.test.d.ts +25 -0
- package/dist/types/capability-deck/index.d.ts +1 -1
- package/dist/types/capability-deck/read-edit-gate.int.test.d.ts +21 -0
- package/dist/types/conductor/bash-guard.d.ts +106 -0
- package/dist/types/conductor/bash-guard.test.d.ts +17 -0
- package/dist/types/conductor/conductor.d.ts +37 -6
- package/dist/types/conductor/contract.d.ts +214 -2
- package/dist/types/conductor/diagnostics.d.ts +183 -0
- package/dist/types/conductor/diagnostics.test.d.ts +10 -0
- package/dist/types/conductor/index.d.ts +4 -1
- package/dist/types/conductor/permission-gate.integration.test.d.ts +22 -0
- package/dist/types/conductor/permission-wiring.test.d.ts +14 -0
- package/dist/types/conductor/permissions.d.ts +217 -0
- package/dist/types/conductor/permissions.test.d.ts +12 -0
- package/dist/types/conductor/plan-mode.integration.test.d.ts +23 -0
- package/dist/types/conductor/post-edit-diagnostics.test.d.ts +13 -0
- package/dist/types/conductor/transcript-store/serialize.test.d.ts +10 -0
- package/dist/types/conductor/transcript-store/store.d.ts +18 -0
- package/dist/types/console/components/StatusBar.d.ts +14 -3
- package/dist/types/console/components/WorkingIndicator.d.ts +44 -0
- package/dist/types/console/components/WorkingIndicator.test.d.ts +9 -0
- package/dist/types/console/contract.d.ts +2 -1
- package/dist/types/console/input/keymap.d.ts +10 -1
- package/dist/types/console/overlays/approval-queue.d.ts +71 -0
- package/dist/types/console/overlays/approval.d.ts +104 -0
- package/dist/types/console/overlays/approval.test.d.ts +17 -0
- package/dist/types/console/overlays/host.d.ts +4 -3
- package/dist/types/console/overlays/index.d.ts +2 -0
- package/dist/types/guardrails.d.ts +33 -0
- package/dist/types/launch/contract.d.ts +2 -0
- package/dist/types/launch/index.d.ts +1 -1
- package/dist/types/launch/oauth.d.ts +13 -0
- package/dist/types/settings/contract.d.ts +47 -0
- package/dist/types/settings/index.d.ts +2 -2
- package/dist/types/window-budget/condenser.d.ts +15 -1
- package/dist/types/window-budget/index.d.ts +3 -1
- package/dist/types/window-budget/microcompact.d.ts +68 -0
- package/dist/types/window-budget/microcompact.test.d.ts +16 -0
- package/dist/types/window-budget/rehydrate.d.ts +56 -0
- package/dist/types/workspace/brand.d.ts +1 -1
- package/package.json +14 -3
|
@@ -11,3 +11,5 @@ export { OverlayHost, type OverlayHostProps } from "./host";
|
|
|
11
11
|
export { PickerOverlays, type OverlayGroupProps } from "./pickers";
|
|
12
12
|
export { SessionOverlays } from "./sessions";
|
|
13
13
|
export { AuthOverlays } from "./auth";
|
|
14
|
+
export { ApprovalOverlays, choiceFromId, readApprovalPayload, summarizeInput, APPROVAL_CHOICES, DISMISS_CHOICE, type ApprovalRequest, type ApprovalPayload, } from "./approval";
|
|
15
|
+
export { approvalQueueReducer, activeApproval, EMPTY_APPROVAL_QUEUE, type ApprovalEntry, type ApprovalQueueState, type ApprovalQueueEvent, } from "./approval-queue";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin guardrails entry — the headless, library-consumable surface of the
|
|
3
|
+
* indusagi coding agent.
|
|
4
|
+
*
|
|
5
|
+
* This barrel re-exports ONLY the deterministic guardrail primitives (the
|
|
6
|
+
* permission rule engine, the bash blocklist, prompt assembly, deck
|
|
7
|
+
* provisioning, and the read-before-edit / checkpoint stores). It deliberately
|
|
8
|
+
* imports from the LEAF modules — never the root `src/index.ts` barrel — so a
|
|
9
|
+
* consumer can pull in the guardrails without dragging in the Ink/React console
|
|
10
|
+
* tree (`export * as consoleUi from "./console"` lives only on the root barrel).
|
|
11
|
+
*
|
|
12
|
+
* Reachable from a published install as `indusagi-coding-agent/guardrails`.
|
|
13
|
+
*/
|
|
14
|
+
export { resolveRuleDecision, makeRule, createPermissionGate, READ_ONLY_TOOL_NAMES, EDIT_TOOL_NAMES, } from "./conductor/permissions";
|
|
15
|
+
export type { PermissionRule, PermissionMode, PermissionBehavior, PermissionDecision, } from "./conductor/permissions";
|
|
16
|
+
export { evaluateCatastrophic, bashSubcommandSubjects } from "./conductor/bash-guard";
|
|
17
|
+
export { composeBriefing } from "./briefing/compose";
|
|
18
|
+
export { gatherContextDocs } from "./briefing/context-docs";
|
|
19
|
+
export type { AgentTool, BriefingContext, ContextDoc } from "./briefing/contract";
|
|
20
|
+
export { provisionDeck } from "./capability-deck/provision";
|
|
21
|
+
export { createReadStateStore, READ_STATE_HANDLE_KEY, ReadStateStore } from "./boot/runners/read-state";
|
|
22
|
+
export { createCheckpointStore, CHECKPOINT_HANDLE_KEY, CheckpointStore } from "./boot/runners/checkpoint";
|
|
23
|
+
/**
|
|
24
|
+
* The byte-stable refusal emitted when an edit/write is attempted on a file the
|
|
25
|
+
* agent has not read this session.
|
|
26
|
+
*
|
|
27
|
+
* Surfaced here as the guardrails contract for consumers (e.g. eval fixtures)
|
|
28
|
+
* that assert the refusal verbatim. The literal is intentionally duplicated
|
|
29
|
+
* rather than re-exported because the upstream message lives only as a private
|
|
30
|
+
* module-local const in the framework's read-state gate — it is not part of any
|
|
31
|
+
* public barrel. A reword on either side must update both sites, by design.
|
|
32
|
+
*/
|
|
33
|
+
export declare const READ_BEFORE_EDIT_MESSAGE = "File has not been read yet. Read it first before writing to it.";
|
|
@@ -171,6 +171,8 @@ export interface Invocation {
|
|
|
171
171
|
readonly attachments?: Attachments;
|
|
172
172
|
/** Explicit model selector (`--model` / `-m`), provider-qualified or bare. */
|
|
173
173
|
readonly model?: string;
|
|
174
|
+
/** Model to fall back to when the selected model is overloaded mid-turn (`--fallback-model`). */
|
|
175
|
+
readonly fallbackModel?: string;
|
|
174
176
|
/** Named credential account to authenticate the run with (`--account`). */
|
|
175
177
|
readonly account?: string;
|
|
176
178
|
/** Working directory the run is scoped to (`--cwd`); absent means process cwd. */
|
|
@@ -20,7 +20,7 @@ export { runPackageCommand, defaultPackageIo, PACKAGE_COMMANDS, } from "./packag
|
|
|
20
20
|
export type { PackageCommand, PackageIo, PackageCommandOptions, PackageResult, } from "./packages";
|
|
21
21
|
export { runCredentialCommand, defaultCredentialIo, formatCredentialFault, validateApiKey, validateAccountName, findProvider, isOAuthCapable, asSigninMethod, PROVIDER_DIRECTORY, } from "./credentials";
|
|
22
22
|
export type { CredentialIo, CredentialResult, SigninMethod } from "./credentials";
|
|
23
|
-
export { registerBuiltInOAuthProviders, listLoginProviders, startOAuthLogin, openLoginUrl, } from "./oauth";
|
|
23
|
+
export { registerBuiltInOAuthProviders, listLoginProviders, startOAuthLogin, openLoginUrl, hasRegisteredOAuthClientId, } from "./oauth";
|
|
24
24
|
export type { AuthKind, LoginProvider, OAuthLoginResult, } from "./oauth";
|
|
25
25
|
export { printModelCatalog, defaultCatalogIo, registrySource } from "./catalog";
|
|
26
26
|
export type { CatalogIo, CatalogModelSource } from "./catalog";
|
|
@@ -29,6 +29,19 @@ import type { AuthVault } from "./contract";
|
|
|
29
29
|
* that are registered after the call, for callers that want to confirm the set.
|
|
30
30
|
*/
|
|
31
31
|
export declare function registerBuiltInOAuthProviders(): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Whether a registered sign-in provider has a *real* OAuth client id wired in
|
|
34
|
+
* (its env var is set to something other than the framework's sentinel), so its
|
|
35
|
+
* browser sign-in can actually complete.
|
|
36
|
+
*
|
|
37
|
+
* Reads the env var live (not at import time) so a deployer that exports the id
|
|
38
|
+
* before launching `/login` is honored. A provider with no known client-id
|
|
39
|
+
* wiring (i.e. not one of the three sentinel-shipped framework providers) is
|
|
40
|
+
* treated as registered — we only gate the providers we know ship a sentinel.
|
|
41
|
+
*
|
|
42
|
+
* @param providerId the registered sign-in provider id
|
|
43
|
+
*/
|
|
44
|
+
export declare function hasRegisteredOAuthClientId(providerId: string): boolean;
|
|
32
45
|
/** How a provider authenticates in the merged sign-in directory. */
|
|
33
46
|
export type AuthKind = "oauth" | "apiKey";
|
|
34
47
|
/** One row of the merged sign-in directory. */
|
|
@@ -48,6 +48,46 @@ export type DeliveryMode = "all" | "one-at-a-time";
|
|
|
48
48
|
export declare const DELIVERY_MODES: readonly DeliveryMode[];
|
|
49
49
|
/** Narrow an arbitrary value to a known {@link DeliveryMode}. */
|
|
50
50
|
export declare function isDeliveryMode(value: unknown): value is DeliveryMode;
|
|
51
|
+
/**
|
|
52
|
+
* How aggressively the per-tool permission gate auto-allows tool calls before it
|
|
53
|
+
* falls back to asking (or denying):
|
|
54
|
+
*
|
|
55
|
+
* - `default` — consult the allow/ask/deny rules; read-only tools are
|
|
56
|
+
* auto-allowed, anything else with no matching allow rule
|
|
57
|
+
* falls through to `ask`.
|
|
58
|
+
* - `acceptEdits` — additionally auto-allow filesystem edits/writes (the
|
|
59
|
+
* mutating edit/write/multiedit tools), leaving everything
|
|
60
|
+
* else on the `default` path.
|
|
61
|
+
* - `bypass` — allow every tool with no prompt (the unsafe "trust all"
|
|
62
|
+
* mode; `bypassPermissions` is accepted as an alias).
|
|
63
|
+
* - `plan` — deny every mutating tool outright (read-only research only)
|
|
64
|
+
* so the agent can plan without touching the workspace.
|
|
65
|
+
*/
|
|
66
|
+
export type PermissionMode = "default" | "acceptEdits" | "bypass" | "plan" | "bypassPermissions";
|
|
67
|
+
/** The canonical {@link PermissionMode} values, as a frozen tuple for menus/guards. */
|
|
68
|
+
export declare const PERMISSION_MODES: readonly PermissionMode[];
|
|
69
|
+
/** Narrow an arbitrary value to a known {@link PermissionMode} (folds the alias). */
|
|
70
|
+
export declare function isPermissionMode(value: unknown): value is PermissionMode;
|
|
71
|
+
/**
|
|
72
|
+
* The declarative permission policy a session reads at startup.
|
|
73
|
+
*
|
|
74
|
+
* `allow`/`ask`/`deny` are ordered lists of induscode-style rule strings — a bare
|
|
75
|
+
* tool name (`"Bash"`) or a tool name with an argument specifier
|
|
76
|
+
* (`"Bash(npm run test:*)"`). Across tiers the three lists are concatenated, and
|
|
77
|
+
* `deny` wins over `ask` wins over `allow`. `defaultMode` seeds the session's
|
|
78
|
+
* permission mode when no flag overrides it. Every field is optional so an empty
|
|
79
|
+
* `{}` (or an absent `permissions` key) keeps today's allow-all behavior.
|
|
80
|
+
*/
|
|
81
|
+
export interface PermissionSettings {
|
|
82
|
+
/** Rule strings that auto-allow a matching tool call (lowest precedence). */
|
|
83
|
+
allow?: string[];
|
|
84
|
+
/** Rule strings that force a matching tool call to prompt for approval. */
|
|
85
|
+
ask?: string[];
|
|
86
|
+
/** Rule strings that hard-block a matching tool call (highest precedence). */
|
|
87
|
+
deny?: string[];
|
|
88
|
+
/** The permission mode the session opens in when no flag overrides it. */
|
|
89
|
+
defaultMode?: PermissionMode;
|
|
90
|
+
}
|
|
51
91
|
/**
|
|
52
92
|
* Every preference an interactive coding-agent session reads, as one flat,
|
|
53
93
|
* fully-optional record.
|
|
@@ -111,6 +151,13 @@ export interface Preferences {
|
|
|
111
151
|
doubleEscapeAction?: EscapeAction;
|
|
112
152
|
/** Extension-package sources the launcher installs and loads (npm / git / local). */
|
|
113
153
|
extensionPackages?: string[];
|
|
154
|
+
/**
|
|
155
|
+
* The declarative tool-permission policy: ordered allow/ask/deny rule lists and
|
|
156
|
+
* the opening permission mode. Across the project/global tiers the rule lists
|
|
157
|
+
* are concatenated (not overridden) and `deny > ask > allow` decides; an absent
|
|
158
|
+
* value (or an empty object) preserves today's allow-all behavior.
|
|
159
|
+
*/
|
|
160
|
+
permissions?: PermissionSettings;
|
|
114
161
|
}
|
|
115
162
|
/** The set of legal preference keys, derived from {@link Preferences}. */
|
|
116
163
|
export type SettingKey = keyof Preferences;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* {@link PreferenceStore} reader/writer. Console surfaces and boot stages depend
|
|
8
8
|
* on this module rather than reaching into the individual files.
|
|
9
9
|
*/
|
|
10
|
-
export type { Preferences, SettingKey, EscapeAction, DeliveryMode, ThinkingLevel, } from "./contract";
|
|
11
|
-
export { DEFAULT_PREFERENCES, SETTING_KEYS, ESCAPE_ACTIONS, isEscapeAction, DELIVERY_MODES, isDeliveryMode, } from "./contract";
|
|
10
|
+
export type { Preferences, SettingKey, EscapeAction, DeliveryMode, ThinkingLevel, PermissionMode, PermissionSettings, } from "./contract";
|
|
11
|
+
export { DEFAULT_PREFERENCES, SETTING_KEYS, ESCAPE_ACTIONS, isEscapeAction, DELIVERY_MODES, isDeliveryMode, PERMISSION_MODES, isPermissionMode, } from "./contract";
|
|
12
12
|
export { PreferenceStore } from "./manager";
|
|
13
13
|
export type { PreferenceLocations } from "./manager";
|
|
@@ -28,8 +28,22 @@
|
|
|
28
28
|
* matching session-scope entrypoint. Both are re-exported here so callers can
|
|
29
29
|
* summarize a slice directly without building a full {@link Condenser}.
|
|
30
30
|
*/
|
|
31
|
-
import type { AgentMessage, CondenserDeps, Condenser, Summary } from "./contract.js";
|
|
31
|
+
import type { AgentMessage, BudgetPolicy, CondenserDeps, Condenser, Summary } from "./contract.js";
|
|
32
32
|
import { condenseScope, type SummarizeDeps } from "./summarize/index.js";
|
|
33
|
+
/**
|
|
34
|
+
* The fallback {@link BudgetPolicy} when the caller supplies none. These are
|
|
35
|
+
* ordinary configuration defaults — a window-relative ratio plus a token tail —
|
|
36
|
+
* computed from the model's own context window rather than fixed magic numbers.
|
|
37
|
+
* - `triggerRatio` 0.75 — condense once ~three-quarters of the window is used.
|
|
38
|
+
* - `keepRecent` 6000 — keep roughly the last 6k tokens of turns verbatim.
|
|
39
|
+
* - `reserveTokens` 2048 — carve a little headroom off the window first.
|
|
40
|
+
*
|
|
41
|
+
* Exported as {@link AUTO_CONDENSE_POLICY} so the conductor's token gate and the
|
|
42
|
+
* session runner's slice planner share **one** policy — the value the auto-path
|
|
43
|
+
* trigger ({@link isOverBudget}) compares against must equal the value the slice
|
|
44
|
+
* planner ({@link planSlice}) cuts with, or the gate and the cut disagree.
|
|
45
|
+
*/
|
|
46
|
+
export declare const AUTO_CONDENSE_POLICY: BudgetPolicy;
|
|
33
47
|
/**
|
|
34
48
|
* Build a {@link Condenser} from a {@link CondenserDeps} bundle. The result plugs
|
|
35
49
|
* straight into the conductor's `CondenseFn` seam.
|
|
@@ -12,4 +12,6 @@ export type { AgentMessage, BudgetPolicy, CompleteFn, CondensePlan, Condenser, C
|
|
|
12
12
|
export { budgetLimit, estimateMessageTokens, estimateTokens, isOverBudget, planSlice, prefixTokens, } from "./budget/index.js";
|
|
13
13
|
export { buildSummaryPrompt, CONDENSER_BRIEF, flattenTranscript, summarize, } from "./summarize/index.js";
|
|
14
14
|
export type { SummarizeDeps } from "./summarize/index.js";
|
|
15
|
-
export { condense, condenseScope, createCondenser } from "./condenser.js";
|
|
15
|
+
export { AUTO_CONDENSE_POLICY, condense, condenseScope, createCondenser } from "./condenser.js";
|
|
16
|
+
export { CLEARED_TOOL_RESULT, clearStaleToolResults, COMPACTABLE_TOOL_NAMES, } from "./microcompact.js";
|
|
17
|
+
export { rehydrateRecentReads, RESTORED_FILE_PREFIX } from "./rehydrate.js";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Window-budget / microcompact — clear stale tool-result bodies in place.
|
|
3
|
+
*
|
|
4
|
+
* The cheapest token to reclaim is one that is already dead weight: the verbatim
|
|
5
|
+
* body of a tool result the model has long since acted on. Once an agent has
|
|
6
|
+
* read a file, run a search, or executed a command and moved several turns past
|
|
7
|
+
* it, the full body of that result rarely needs to stay in the window — the
|
|
8
|
+
* model's subsequent reasoning already captured what mattered. Yet those bodies
|
|
9
|
+
* (a 2,000-line `read`, a noisy `bash` log, a wide `grep`) dominate the token
|
|
10
|
+
* budget.
|
|
11
|
+
*
|
|
12
|
+
* **Microcompaction** runs *before* a full {@link condense}: it walks the
|
|
13
|
+
* transcript, keeps the last few tool results verbatim (the model may still be
|
|
14
|
+
* working with them), and replaces the body of every older compactable tool
|
|
15
|
+
* result with a short sentinel. This reclaims tokens for free — no model call,
|
|
16
|
+
* no summary round-trip — and often defers (or shrinks the input to) the real
|
|
17
|
+
* condense.
|
|
18
|
+
*
|
|
19
|
+
* Shape note (this rebuild): a tool result is its OWN top-level
|
|
20
|
+
* {@link ToolResultMessage} (`role: 'toolResult'`), not a block nested inside a
|
|
21
|
+
* user message. Clearing one therefore means replacing *that message's*
|
|
22
|
+
* `content` array with a single sentinel text block — never editing a sub-block
|
|
23
|
+
* of some other message. We only clear results whose owning assistant `toolCall`
|
|
24
|
+
* named a compactable tool, so structural/control results stay intact.
|
|
25
|
+
*
|
|
26
|
+
* Behavior-preserving by construction: when there is nothing to clear (no
|
|
27
|
+
* compactable results, or every one is within the keep window) the *same input
|
|
28
|
+
* reference* is returned, so a caller can cheaply detect a no-op.
|
|
29
|
+
*/
|
|
30
|
+
import type { AgentMessage } from "./contract.js";
|
|
31
|
+
/**
|
|
32
|
+
* The placeholder spliced in where a stale tool-result body used to be. Kept
|
|
33
|
+
* short on purpose — the whole point is to reclaim the tokens the original body
|
|
34
|
+
* occupied — while still signalling to the model (and to a human reading the
|
|
35
|
+
* transcript) that real content was elided rather than lost.
|
|
36
|
+
*/
|
|
37
|
+
export declare const CLEARED_TOOL_RESULT = "[Old tool result content cleared]";
|
|
38
|
+
/**
|
|
39
|
+
* The wire names of tools whose results are safe to clear once stale.
|
|
40
|
+
*
|
|
41
|
+
* These are the read-mostly / output-heavy tools whose bodies are reconstructable
|
|
42
|
+
* (re-read the file, re-run the search) and whose stale content the model has
|
|
43
|
+
* already digested. Control/state tools (todo, memory, task) are deliberately
|
|
44
|
+
* absent — their results can carry standing instructions the model still needs.
|
|
45
|
+
*/
|
|
46
|
+
export declare const COMPACTABLE_TOOL_NAMES: ReadonlySet<string>;
|
|
47
|
+
/**
|
|
48
|
+
* Clear the bodies of stale compactable tool results, keeping the most recent
|
|
49
|
+
* `keepRecent` of them verbatim.
|
|
50
|
+
*
|
|
51
|
+
* Walks the transcript, identifies every `role:'toolResult'` message that
|
|
52
|
+
* belongs to a compactable tool call, keeps the last `keepRecent` (floored at 1
|
|
53
|
+
* — `slice(-0)` would keep everything), and replaces each older one's `content`
|
|
54
|
+
* with a single `{ type:'text', text: CLEARED_TOOL_RESULT }` block. Non-tool
|
|
55
|
+
* messages, control-tool results, and already-cleared results are left exactly
|
|
56
|
+
* as they were.
|
|
57
|
+
*
|
|
58
|
+
* Returns the **same input array reference** when nothing was cleared, so the
|
|
59
|
+
* caller can detect a no-op by identity. Otherwise returns a new array; cleared
|
|
60
|
+
* messages are fresh objects (the originals are not mutated).
|
|
61
|
+
*
|
|
62
|
+
* @param messages the active transcript, oldest-first
|
|
63
|
+
* @param opts.keepRecent how many most-recent compactable results to keep
|
|
64
|
+
* verbatim (default {@link DEFAULT_KEEP_RECENT}, floored at 1)
|
|
65
|
+
*/
|
|
66
|
+
export declare function clearStaleToolResults(messages: AgentMessage[], opts?: {
|
|
67
|
+
keepRecent?: number;
|
|
68
|
+
}): AgentMessage[];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Window-budget / microcompact + rehydrate — unit tests for item #18.
|
|
3
|
+
*
|
|
4
|
+
* Three layers ride the condense path:
|
|
5
|
+
* 1. microcompact ({@link clearStaleToolResults}) — blank older completed
|
|
6
|
+
* tool-result bodies to a sentinel, keeping the last N verbatim; no-op
|
|
7
|
+
* (same reference) otherwise.
|
|
8
|
+
* 2. rehydration ({@link rehydrateRecentReads}) — re-attach the most-recently
|
|
9
|
+
* dropped file reads as synthetic user messages, deduped against the kept
|
|
10
|
+
* tail, honoring maxFiles / tokenBudget.
|
|
11
|
+
* 3. circuit breaker — covered in the conductor suite (3 throwing condense
|
|
12
|
+
* calls stop further attempts, a success resets).
|
|
13
|
+
*
|
|
14
|
+
* Pure + network-free: no model is bound anywhere here.
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Window-budget / rehydrate — re-attach recently-read files after a condense.
|
|
3
|
+
*
|
|
4
|
+
* A full {@link condense} folds the dropped head of the transcript into one
|
|
5
|
+
* summary message. That digest captures *what happened*, but it necessarily
|
|
6
|
+
* loses the verbatim bodies of files the agent had just read — and those bodies
|
|
7
|
+
* are often exactly the working context the next turn needs (the file it was
|
|
8
|
+
* mid-edit on, the config it was reasoning about). Re-reading them costs a tool
|
|
9
|
+
* round-trip and, worse, the model may not realize it needs to.
|
|
10
|
+
*
|
|
11
|
+
* **Post-compact rehydration** softens that cliff: after the slice is dropped,
|
|
12
|
+
* it scans the *dropped* messages for `read` results, takes the most-recent
|
|
13
|
+
* handful that aren't already visible in the kept tail, and re-attaches their
|
|
14
|
+
* bodies as synthetic user messages ("[Restored file after compaction] …") under
|
|
15
|
+
* a token budget. The model keeps its working files without a re-read, while the
|
|
16
|
+
* older surrounding chatter still gets compressed away.
|
|
17
|
+
*
|
|
18
|
+
* Shape note (this rebuild): a read's *body* lives in its own top-level
|
|
19
|
+
* {@link ToolResultMessage} (`role:'toolResult'`), and the read's *path* lives in
|
|
20
|
+
* the owning assistant `toolCall.arguments.path` (the read tool's arg is `path`,
|
|
21
|
+
* not `file_path`). We pair each dropped `read` call with its result by
|
|
22
|
+
* `toolCallId`, dedup against paths still visible in the kept tail, and emit the
|
|
23
|
+
* most-recent survivors under a token cap.
|
|
24
|
+
*
|
|
25
|
+
* Behavior-preserving by construction: returns an empty array whenever there is
|
|
26
|
+
* nothing to restore (no dropped reads, all already visible, or the budget is
|
|
27
|
+
* exhausted), so the caller can splice the result unconditionally.
|
|
28
|
+
*/
|
|
29
|
+
import type { AgentMessage } from "./contract.js";
|
|
30
|
+
/** Prefix marking a synthetic message that restores a file body post-compaction. */
|
|
31
|
+
export declare const RESTORED_FILE_PREFIX = "[Restored file after compaction]";
|
|
32
|
+
/**
|
|
33
|
+
* Build the synthetic user messages that re-attach the most-recently-read files
|
|
34
|
+
* dropped by a condense.
|
|
35
|
+
*
|
|
36
|
+
* Scans `dropped` for `read` calls + result bodies, drops any path still visible
|
|
37
|
+
* in `kept`, sorts the survivors most-recent-first, takes up to `maxFiles`, and
|
|
38
|
+
* emits each as a `user` message of the form
|
|
39
|
+
* `"[Restored file after compaction] <path>\n<body>"` — stopping once the running
|
|
40
|
+
* token estimate would exceed `tokenBudget`. The returned messages are ordered
|
|
41
|
+
* oldest-restored-first so they read naturally when spliced after the summary.
|
|
42
|
+
*
|
|
43
|
+
* Returns an **empty array** when nothing is restorable, so the caller can splice
|
|
44
|
+
* the result unconditionally (`[summary, ...restored, ...kept]`).
|
|
45
|
+
*
|
|
46
|
+
* @param dropped the condensed-away head slice (original bodies, pre-summary)
|
|
47
|
+
* @param kept the verbatim tail that survives the condense
|
|
48
|
+
* @param opts.maxFiles cap on restored files (default {@link DEFAULT_MAX_FILES})
|
|
49
|
+
* @param opts.tokenBudget token ceiling for the restored block (default {@link DEFAULT_TOKEN_BUDGET})
|
|
50
|
+
* @param opts.readToolName wire name of the read tool (default `"read"`)
|
|
51
|
+
*/
|
|
52
|
+
export declare function rehydrateRecentReads(dropped: AgentMessage[], kept: AgentMessage[], opts?: {
|
|
53
|
+
maxFiles?: number;
|
|
54
|
+
tokenBudget?: number;
|
|
55
|
+
readToolName?: string;
|
|
56
|
+
}): AgentMessage[];
|
|
@@ -23,4 +23,4 @@ export declare const BRAND: Brand;
|
|
|
23
23
|
* Single source of truth: bump this one line per release. Co-located with the
|
|
24
24
|
* brand so `boot` reads it without importing the index barrel.
|
|
25
25
|
*/
|
|
26
|
-
export declare const VERSION = "0.
|
|
26
|
+
export declare const VERSION = "0.2.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "indusagi-coding-agent",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Indusagi coding agent — a terminal-first AI coding agent, built from scratch on the indusagi framework.",
|
|
5
5
|
"author": "Varun Israni",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,12 +22,23 @@
|
|
|
22
22
|
"engines": {
|
|
23
23
|
"node": ">=20"
|
|
24
24
|
},
|
|
25
|
-
"main": "./dist/
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
26
|
"types": "./dist/types/index.d.ts",
|
|
27
27
|
"bin": {
|
|
28
28
|
"indus": "dist/entry.js",
|
|
29
29
|
"indusagi": "dist/entry.js"
|
|
30
30
|
},
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/types/index.d.ts",
|
|
34
|
+
"import": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./guardrails": {
|
|
37
|
+
"types": "./dist/types/guardrails.d.ts",
|
|
38
|
+
"import": "./dist/guardrails.js"
|
|
39
|
+
},
|
|
40
|
+
"./package.json": "./package.json"
|
|
41
|
+
},
|
|
31
42
|
"files": [
|
|
32
43
|
"dist",
|
|
33
44
|
"CHANGELOG.md",
|
|
@@ -46,7 +57,7 @@
|
|
|
46
57
|
"@sinclair/typebox": "^0.34.49",
|
|
47
58
|
"chalk": "^5.6.2",
|
|
48
59
|
"highlight.js": "^11.11.1",
|
|
49
|
-
"indusagi": "^0.
|
|
60
|
+
"indusagi": "^0.13.0",
|
|
50
61
|
"ink": "^5.2.1",
|
|
51
62
|
"jiti": "^2.7.0",
|
|
52
63
|
"marked": "^18.0.4",
|