indusagi-coding-agent 0.2.11 → 0.2.13
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/.env.review.example +25 -0
- package/CHANGELOG.md +6 -0
- package/README.md +11 -0
- package/REVIEW_AGENT.md +137 -0
- package/dist/entry.js +103 -25
- package/dist/guardrails.js +67 -1
- package/dist/index.js +1034 -66
- package/dist/review-agent.js +4177 -0
- package/dist/review-server.js +4349 -0
- package/package.json +15 -10
- package/src/boot/contract.ts +1 -1
- package/src/boot/runners/session.ts +8 -4
- package/src/boot/stages.ts +3 -6
- package/src/conductor/catalog/catalog.ts +3 -1
- package/src/conductor/conductor.ts +10 -5
- package/src/conductor/contract.ts +8 -4
- package/src/conductor/index.ts +1 -1
- package/src/conductor/signal-hub/translate.ts +2 -2
- package/src/console/slash/commands/workbench.ts +2 -2
- package/src/index.ts +1 -0
- package/src/launch/catalog.ts +4 -2
- package/src/launch/contract.ts +1 -0
- package/src/launch/invocation/flags.ts +1 -1
- package/src/launch/invocation/read.ts +5 -4
- package/src/models.ts +90 -0
- package/src/review-agent/config.ts +75 -0
- package/src/review-agent/contract.ts +59 -0
- package/src/review-agent/github.ts +242 -0
- package/src/review-agent/index.ts +8 -0
- package/src/review-agent/prompt.ts +132 -0
- package/src/review-agent/reviewer.ts +118 -0
- package/src/review-agent/server.ts +221 -0
- package/src/review-agent/service.ts +165 -0
- package/src/review-agent/supabase.ts +209 -0
- package/src/review-agent/webhook.ts +76 -0
- package/src/workspace/brand.ts +1 -1
- package/supabase/migrations/20260827031803_indusagi_review_agent.sql +109 -0
- package/supabase/migrations/20260827035747_atomic_webhook_enqueue.sql +75 -0
- package/supabase/migrations/20260827040104_check_run_reconciliation.sql +8 -0
- package/supabase/migrations/20260827041049_owned_job_leases.sql +80 -0
- package/supabase/migrations/20260827042500_static_token_webhooks.sql +75 -0
- package/src/conductor/signal-hub/translate.test.ts +0 -81
- package/src/entry.test.ts +0 -17
- package/src/integrations/sarvam/sarvam.test.ts +0 -60
- package/src/integrations/zoho/zoho.test.ts +0 -50
- package/src/launch/oauth.test.ts +0 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "indusagi-coding-agent",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.13",
|
|
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": "AGPL-3.0",
|
|
@@ -22,44 +22,49 @@
|
|
|
22
22
|
"node": ">=20"
|
|
23
23
|
},
|
|
24
24
|
"main": "./dist/index.js",
|
|
25
|
-
"types": "./dist/types/index.d.ts",
|
|
26
25
|
"bin": {
|
|
27
26
|
"indus": "dist/entry.js",
|
|
28
|
-
"indusagi": "dist/entry.js"
|
|
27
|
+
"indusagi": "dist/entry.js",
|
|
28
|
+
"indusagi-review": "dist/review-server.js"
|
|
29
29
|
},
|
|
30
30
|
"exports": {
|
|
31
31
|
".": {
|
|
32
|
-
"types": "./dist/types/index.d.ts",
|
|
33
32
|
"import": "./dist/index.js"
|
|
34
33
|
},
|
|
35
34
|
"./guardrails": {
|
|
36
|
-
"types": "./dist/types/guardrails.d.ts",
|
|
37
35
|
"import": "./dist/guardrails.js"
|
|
38
36
|
},
|
|
37
|
+
"./review-agent": {
|
|
38
|
+
"import": "./dist/review-agent.js"
|
|
39
|
+
},
|
|
39
40
|
"./package.json": "./package.json"
|
|
40
41
|
},
|
|
41
42
|
"files": [
|
|
42
43
|
"dist",
|
|
43
44
|
"src",
|
|
45
|
+
"supabase",
|
|
46
|
+
".env.review.example",
|
|
44
47
|
"CHANGELOG.md",
|
|
45
48
|
"CREDITS.md",
|
|
46
49
|
"NOTICE",
|
|
47
|
-
"README.md"
|
|
50
|
+
"README.md",
|
|
51
|
+
"REVIEW_AGENT.md"
|
|
48
52
|
],
|
|
49
53
|
"scripts": {
|
|
50
54
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
51
55
|
"build": "node build.mjs",
|
|
52
|
-
"lineage-scan": "node scripts/lineage-scan.mjs",
|
|
53
56
|
"test": "vitest --run",
|
|
54
57
|
"test:watch": "vitest",
|
|
58
|
+
"prepublishOnly": "npm test && npm run build",
|
|
55
59
|
"start": "node dist/entry.js",
|
|
56
|
-
"start:help": "node dist/entry.js --help"
|
|
60
|
+
"start:help": "node dist/entry.js --help",
|
|
61
|
+
"review:server": "node dist/review-server.js"
|
|
57
62
|
},
|
|
58
63
|
"dependencies": {
|
|
59
64
|
"@sinclair/typebox": "^0.34.49",
|
|
60
65
|
"chalk": "^5.6.2",
|
|
61
66
|
"highlight.js": "^11.11.1",
|
|
62
|
-
"indusagi": "
|
|
67
|
+
"indusagi": "0.13.11",
|
|
63
68
|
"ink": "^5.2.1",
|
|
64
69
|
"jiti": "^2.7.0",
|
|
65
70
|
"marked": "^18.0.4",
|
|
@@ -75,4 +80,4 @@
|
|
|
75
80
|
"typescript": "^5.7.2",
|
|
76
81
|
"vitest": "^2.1.8"
|
|
77
82
|
}
|
|
78
|
-
}
|
|
83
|
+
}
|
package/src/boot/contract.ts
CHANGED
|
@@ -131,7 +131,7 @@ export interface Invocation {
|
|
|
131
131
|
readonly cwd?: string;
|
|
132
132
|
/** Named credential account to authenticate with (`--account`). */
|
|
133
133
|
readonly account?: string;
|
|
134
|
-
/** Reasoning effort (`--thinking`): off / minimal / low / medium / high / xhigh. */
|
|
134
|
+
/** Reasoning effort (`--thinking`): off / minimal / low / medium / high / xhigh / max (model-dependent). */
|
|
135
135
|
readonly thinking?: string;
|
|
136
136
|
/** Replacement system prompt (`--system`); a path is read as a file, else literal text. */
|
|
137
137
|
readonly system?: string;
|
|
@@ -58,6 +58,8 @@ import { createAuthVault } from "../auth-vault.js";
|
|
|
58
58
|
import { TOKEN_FILE, hasValidServerToken, getServerToken } from "../server-token.js";
|
|
59
59
|
import { resolveServerMode, resolveServerGatewayUrls, serverTierProviders } from "./server-mode.js";
|
|
60
60
|
import { DiskMemoryStore, memoryDirFor, loadMemoryDoc } from "./memdir.js";
|
|
61
|
+
import { isReasoningEffortSupported } from "../../models.js";
|
|
62
|
+
import type { CodingAgentThinkingLevel } from "../../conductor/contract.js";
|
|
61
63
|
import { buildAddonHost, wrapToolsWithAddons, concatAddonTools } from "./addon-wiring.js";
|
|
62
64
|
import type { BootContext, Invocation } from "../contract.js";
|
|
63
65
|
|
|
@@ -219,10 +221,12 @@ function buildKeyResolver(
|
|
|
219
221
|
};
|
|
220
222
|
}
|
|
221
223
|
|
|
222
|
-
const THINKING_LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh"]);
|
|
224
|
+
const THINKING_LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
223
225
|
|
|
224
|
-
function resolveThinkingLevel(raw: string | undefined):
|
|
225
|
-
return raw !== undefined && THINKING_LEVELS.has(raw)
|
|
226
|
+
export function resolveThinkingLevel(raw: string | undefined, modelId?: string): CodingAgentThinkingLevel | undefined {
|
|
227
|
+
return raw !== undefined && THINKING_LEVELS.has(raw) && isReasoningEffortSupported(modelId, raw)
|
|
228
|
+
? raw as CodingAgentThinkingLevel
|
|
229
|
+
: undefined;
|
|
226
230
|
}
|
|
227
231
|
|
|
228
232
|
function resolveText(value: string): string {
|
|
@@ -597,7 +601,7 @@ export async function buildSessionConductor(ctx: BootContext) {
|
|
|
597
601
|
const tools = wrapToolsWithAddons(concatAddonTools(deck, addons), addons);
|
|
598
602
|
const skills = gatherModelSkills(cwd);
|
|
599
603
|
const system = composeSystem(tools, inv, cwd, skills, memoryDoc);
|
|
600
|
-
const thinking = resolveThinkingLevel(inv.thinking);
|
|
604
|
+
const thinking = resolveThinkingLevel(inv.thinking, modelId);
|
|
601
605
|
const fallbackModelId = inv.fallbackModelId;
|
|
602
606
|
const planReachable = inv.mode === "repl";
|
|
603
607
|
const canUseTool = buildPermissionGate(permissionRules, permissionMode, tools, planReachable);
|
package/src/boot/stages.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { tokenizeInvocation } from "./invocation.js";
|
|
|
29
29
|
import { applyUpgrades } from "./upgrade/apply.js";
|
|
30
30
|
import { ensureDirs } from "../workspace/locator.js";
|
|
31
31
|
import type { BootContext, Stage } from "./contract.js";
|
|
32
|
+
import { createCodingAgentModelRegistry } from "../models.js";
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Materialise the resolved workspace directories on disk.
|
|
@@ -110,11 +111,7 @@ async function loadDefaultSettings(): Promise<unknown> {
|
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
async function loadModelRegistry(): Promise<unknown> {
|
|
113
|
-
|
|
114
|
-
if (typeof mod.ModelRegistry === "function") {
|
|
115
|
-
return new (mod.ModelRegistry as new () => unknown)();
|
|
116
|
-
}
|
|
117
|
-
return mod.modelRegistry;
|
|
114
|
+
return createCodingAgentModelRegistry();
|
|
118
115
|
}
|
|
119
116
|
|
|
120
117
|
/**
|
|
@@ -164,4 +161,4 @@ export async function runStages(initial: BootContext, stages: readonly Stage[] =
|
|
|
164
161
|
return ctx;
|
|
165
162
|
}
|
|
166
163
|
|
|
167
|
-
export { locateWorkspace, upgrade, buildInvocation, resolveResources, selectRunnerStage };
|
|
164
|
+
export { locateWorkspace, upgrade, buildInvocation, resolveResources, selectRunnerStage, loadModelRegistry };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { getModels, getProviders } from "indusagi/ai";
|
|
2
|
+
import { getModels, getProviders, modelRegistry } from "indusagi/ai";
|
|
3
3
|
import type { KnownProvider, Model } from "indusagi/ai";
|
|
4
4
|
import type { ModelCardRef } from "../contract.js";
|
|
5
|
+
import { applyCodingAgentModelOverlay } from "../../models.js";
|
|
5
6
|
|
|
6
7
|
export interface CatalogCard {
|
|
7
8
|
readonly id: string;
|
|
@@ -29,6 +30,7 @@ const rawModelGate = z.object({
|
|
|
29
30
|
});
|
|
30
31
|
|
|
31
32
|
const EXCLUDED_PROVIDERS = new Set(["mock"]);
|
|
33
|
+
applyCodingAgentModelOverlay(modelRegistry);
|
|
32
34
|
const frameworkSource: CatalogSource = {
|
|
33
35
|
providers: () => getProviders().filter((provider) => !EXCLUDED_PROVIDERS.has(String(provider))),
|
|
34
36
|
models: (provider) => getModels(provider as KnownProvider),
|
|
@@ -4,7 +4,7 @@ import { createZeroUsage } from "indusagi/ai";
|
|
|
4
4
|
import type { Model, Usage } from "indusagi/ai";
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { conductorFault } from "./contract.js";
|
|
7
|
-
import type { ApprovalResolver, ConductorFault, ConductorPhase, ConductorState, PermissionMode, SessionConductor, SessionConductorOptions, SessionHead } from "./contract.js";
|
|
7
|
+
import type { ApprovalResolver, CodingAgentThinkingLevel, ConductorFault, ConductorPhase, ConductorState, PermissionMode, SessionConductor, SessionConductorOptions, SessionHead } from "./contract.js";
|
|
8
8
|
import { ModelCatalog, ModelMatcher } from "./catalog/index.js";
|
|
9
9
|
import type { CatalogCard } from "./catalog/index.js";
|
|
10
10
|
import { SignalHub, translateAgentEvent } from "./signal-hub/index.js";
|
|
@@ -15,6 +15,7 @@ import { writePlan } from "../capability-deck/cards/plan-file.js";
|
|
|
15
15
|
import { AUTO_CONDENSE_POLICY, isOverBudget } from "../window-budget/index.js";
|
|
16
16
|
import { DiagnosticsEngine, formatDiagnosticsSummary, hasEslintConfig, hasTsConfig, isTypeScriptOrJs, runEslintDiagnostics, runTscDiagnostics } from "./diagnostics.js";
|
|
17
17
|
import type { DiagnosticsConfig } from "./diagnostics.js";
|
|
18
|
+
import { isReasoningEffortSupported, type CodingAgentModel } from "../models.js";
|
|
18
19
|
|
|
19
20
|
export interface AgentLike {
|
|
20
21
|
subscribe(fn: (event: AgentEvent) => void): () => void;
|
|
@@ -62,7 +63,7 @@ export function reduceState(prev: ConductorState, action: StateAction): Conducto
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
const DEFAULT_RETRY = { maxAttempts: 2, baseDelayMs: 250 };
|
|
65
|
-
const
|
|
66
|
+
const DEFAULT_THINKING_LADDER: readonly CodingAgentThinkingLevel[] = ["off", "low", "medium", "high"];
|
|
66
67
|
const PERMISSION_CYCLE: PermissionMode[] = ["default", "acceptEdits", "plan", "bypass"];
|
|
67
68
|
|
|
68
69
|
function newSessionId(): string { return `s_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`; }
|
|
@@ -73,6 +74,10 @@ function accumulateUsage(a: Usage, b: Usage): Usage {
|
|
|
73
74
|
function errorText(error: unknown): string { return error instanceof Error ? error.message : String(error); }
|
|
74
75
|
function isTransient(error: unknown): boolean { const text = errorText(error).toLowerCase(); return ["429", "rate limit", "overloaded", "timeout", "timed out", "econnreset", "etimedout", "503", "502", "500", "unavailable", "temporarily"].some((part) => text.includes(part)); }
|
|
75
76
|
function isOverloadFault(error: unknown): boolean { const text = errorText(error).toLowerCase(); return text.includes("529") || text.includes("overload"); }
|
|
77
|
+
function thinkingLadderForModel(model: Model<any> | undefined): readonly CodingAgentThinkingLevel[] {
|
|
78
|
+
const efforts = (model as CodingAgentModel | undefined)?.reasoningEfforts;
|
|
79
|
+
return efforts?.length ? efforts : DEFAULT_THINKING_LADDER;
|
|
80
|
+
}
|
|
76
81
|
|
|
77
82
|
export function withGatewayBaseUrl<M extends { baseUrl: string; provider: string }>(model: M, gatewayBaseUrls: Record<string, string> | undefined): M {
|
|
78
83
|
const url = gatewayBaseUrls?.[model.provider];
|
|
@@ -132,7 +137,7 @@ class SessionConductorImpl implements SessionConductor {
|
|
|
132
137
|
#draining = false;
|
|
133
138
|
#queue: { mode: "steer" | "followUp"; text: string }[] = [];
|
|
134
139
|
#boundModel?: Model<any>;
|
|
135
|
-
#thinking:
|
|
140
|
+
#thinking: CodingAgentThinkingLevel;
|
|
136
141
|
#prePlanMode?: PermissionMode;
|
|
137
142
|
#sessionName?: string;
|
|
138
143
|
#gatewayBaseUrls?: Record<string, string>;
|
|
@@ -218,8 +223,8 @@ class SessionConductorImpl implements SessionConductor {
|
|
|
218
223
|
stats() { const messages = this.agent.state.messages; let userMessages = 0, assistantMessages = 0, toolCalls = 0, toolResults = 0; for (const message of messages) { if (message.role === "user") userMessages++; else if (message.role === "assistant") { assistantMessages++; for (const part of message.content ?? []) if (typeof part === "object" && part && part.type === "toolCall") toolCalls++; } else if (message.role === "toolResult") toolResults++; } const usage = this.#state.usage; return { sessionId: this.#state.head.sessionId, userMessages, assistantMessages, toolCalls, toolResults, totalMessages: messages.length, tokens: { input: usage.input, output: usage.output, cacheRead: usage.cacheRead, cacheWrite: usage.cacheWrite, total: usage.totalTokens }, cost: usage.cost.total }; }
|
|
219
224
|
costReport() { const stats = this.stats(); return [`Total cost: $${stats.cost.toFixed(4)}`, `Total tokens: ${stats.tokens.total}`, ` Input: ${stats.tokens.input}`, ` Output: ${stats.tokens.output}`, ` Cache read: ${stats.tokens.cacheRead}`, ` Cache write: ${stats.tokens.cacheWrite}`].join("\n"); }
|
|
220
225
|
thinkingLevel() { return this.#thinking; }
|
|
221
|
-
setThinkingLevel(level:
|
|
222
|
-
cycleThinkingLevel() { const next =
|
|
226
|
+
setThinkingLevel(level: CodingAgentThinkingLevel) { if (!isReasoningEffortSupported(this.#boundModel?.id, level)) return; this.#thinking = level; this.agent.setThinkingLevel?.(level as ThinkingLevel); }
|
|
227
|
+
cycleThinkingLevel() { const ladder = thinkingLadderForModel(this.#boundModel); const next = ladder[(ladder.indexOf(this.#thinking) + 1) % ladder.length]; this.setThinkingLevel(next); return next; }
|
|
223
228
|
permissionMode() { return this.modeHolder.mode; }
|
|
224
229
|
setPermissionMode(mode: PermissionMode) { this.modeHolder.mode = mode; }
|
|
225
230
|
setApprovalResolver(resolver: ApprovalResolver | undefined) { this.approvalHolder.resolver = resolver; }
|
|
@@ -40,6 +40,7 @@ import type { AgentMessage, AgentTool, CanUseToolFn, ThinkingLevel } from "indus
|
|
|
40
40
|
import type { KnownProvider, Model, Usage } from "indusagi/ai";
|
|
41
41
|
import type { PermissionMode } from "../settings.js";
|
|
42
42
|
import type { ApprovalResolver } from "./permissions.js";
|
|
43
|
+
export type CodingAgentThinkingLevel = ThinkingLevel | "max";
|
|
43
44
|
/** Re-exported framework vocabulary that conductor consumers routinely need. */
|
|
44
45
|
export type { AgentMessage, AgentTool, CanUseToolFn, ThinkingLevel, Model, Usage, KnownProvider };
|
|
45
46
|
/** Re-exported permission vocabulary, so conductor consumers get it in one import. */
|
|
@@ -131,6 +132,7 @@ export type SessionSignal = {
|
|
|
131
132
|
readonly kind: "tool_start";
|
|
132
133
|
readonly id: string;
|
|
133
134
|
readonly name: string;
|
|
135
|
+
readonly args: unknown;
|
|
134
136
|
} | {
|
|
135
137
|
readonly kind: "tool_update";
|
|
136
138
|
readonly id: string;
|
|
@@ -140,6 +142,8 @@ export type SessionSignal = {
|
|
|
140
142
|
readonly kind: "tool_end";
|
|
141
143
|
readonly id: string;
|
|
142
144
|
readonly ok: boolean;
|
|
145
|
+
readonly result: unknown;
|
|
146
|
+
readonly isError: boolean;
|
|
143
147
|
} | {
|
|
144
148
|
readonly kind: "turn_end";
|
|
145
149
|
readonly usage: Usage;
|
|
@@ -431,7 +435,7 @@ export interface SessionConductorOptions {
|
|
|
431
435
|
/** Tools made available to the agent for this session. */
|
|
432
436
|
readonly tools?: AgentTool[];
|
|
433
437
|
/** Initial reasoning effort for models that support it. */
|
|
434
|
-
readonly thinking?:
|
|
438
|
+
readonly thinking?: CodingAgentThinkingLevel;
|
|
435
439
|
/**
|
|
436
440
|
* Canonical id of a model to fall back to when the bound model is overloaded
|
|
437
441
|
* (HTTP 529 / "overloaded") mid-turn (`--fallback-model`). When set, an
|
|
@@ -715,19 +719,19 @@ export interface SessionConductor {
|
|
|
715
719
|
*/
|
|
716
720
|
costReport(): string;
|
|
717
721
|
/** The reasoning effort currently applied to the session. */
|
|
718
|
-
thinkingLevel():
|
|
722
|
+
thinkingLevel(): CodingAgentThinkingLevel;
|
|
719
723
|
/**
|
|
720
724
|
* Set the reasoning effort for subsequent turns. Applied to the agent when it
|
|
721
725
|
* exposes a setter; otherwise stored and applied on the next model bind.
|
|
722
726
|
*
|
|
723
727
|
* @param level the reasoning effort to apply
|
|
724
728
|
*/
|
|
725
|
-
setThinkingLevel(level:
|
|
729
|
+
setThinkingLevel(level: CodingAgentThinkingLevel): void;
|
|
726
730
|
/**
|
|
727
731
|
* Advance the reasoning effort to the next level in the cycle, applying it, and
|
|
728
732
|
* return the newly-selected level.
|
|
729
733
|
*/
|
|
730
|
-
cycleThinkingLevel():
|
|
734
|
+
cycleThinkingLevel(): CodingAgentThinkingLevel;
|
|
731
735
|
/**
|
|
732
736
|
* The permission mode currently in effect. The live `canUseTool` gate reads
|
|
733
737
|
* this on every tool call, so the value reflects the most recent
|
package/src/conductor/index.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* land; consumers import the conductor surface from `src/conductor` rather than
|
|
17
17
|
* reaching into individual modules.
|
|
18
18
|
*/
|
|
19
|
-
export type { FaultKind, ConductorFault, SessionSignal, SignalKind, SignalOf, SignalHandler, TranscriptSchema, TranscriptRole, TranscriptEntry, SessionHead, ModelCardRef, MatchQuery, ConductorPhase, ConductorState, QueueMode, QueuedInput, SessionStats, ExecuteBashOptions, BashOutcome, SessionConductor, SessionConductorOptions, CondenseOutcome, AgentMessage, AgentTool, CanUseToolFn, ThinkingLevel, Model, Usage, KnownProvider, PermissionMode, } from "./contract.js";
|
|
19
|
+
export type { FaultKind, ConductorFault, SessionSignal, SignalKind, SignalOf, SignalHandler, TranscriptSchema, TranscriptRole, TranscriptEntry, SessionHead, ModelCardRef, MatchQuery, ConductorPhase, ConductorState, QueueMode, QueuedInput, SessionStats, ExecuteBashOptions, BashOutcome, SessionConductor, SessionConductorOptions, CondenseOutcome, AgentMessage, AgentTool, CanUseToolFn, ThinkingLevel, CodingAgentThinkingLevel, Model, Usage, KnownProvider, PermissionMode, } from "./contract.js";
|
|
20
20
|
export { conductorFault, TRANSCRIPT_SCHEMA } from "./contract.js";
|
|
21
21
|
export { createSessionConductor, reduceState, noopCondense, withGatewayBaseUrl, type AgentLike, type ConductorDeps, type CondenseFn, type RetryPolicy, } from "./conductor.js";
|
|
22
22
|
export { SignalHub, translateAgentEvent, type SignalHubOptions } from "./signal-hub/index.js";
|
|
@@ -52,7 +52,7 @@ export const TRANSLATOR_TABLE: TranslatorTable = {
|
|
|
52
52
|
},
|
|
53
53
|
turn_end: (event) => [{ kind: "turn_end", usage: usageOf(event.message) }],
|
|
54
54
|
message_update: (event) => translateStreamEvent(event.assistantMessageEvent),
|
|
55
|
-
tool_execution_start: (event) => [{ kind: "tool_start", id: event.toolCallId, name: event.toolName, args: event.args }
|
|
55
|
+
tool_execution_start: (event) => [{ kind: "tool_start", id: event.toolCallId, name: event.toolName, args: event.args }],
|
|
56
56
|
tool_execution_update: (event) => [{
|
|
57
57
|
kind: "tool_update",
|
|
58
58
|
id: event.toolCallId,
|
|
@@ -65,7 +65,7 @@ export const TRANSLATOR_TABLE: TranslatorTable = {
|
|
|
65
65
|
ok: !event.isError,
|
|
66
66
|
result: event.result,
|
|
67
67
|
isError: event.isError,
|
|
68
|
-
}
|
|
68
|
+
}],
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
export function translateAgentEvent(event: AgentEvent): SessionSignal[] {
|
|
@@ -147,7 +147,7 @@ const HOTKEYS: Array<[string, string]> = [
|
|
|
147
147
|
["Tab", "Accept the highlighted completion"],
|
|
148
148
|
["Ctrl+C", "Interrupt the running turn"],
|
|
149
149
|
["Ctrl+T", "Toggle reasoning / thinking rows"],
|
|
150
|
-
["/thinking", "Cycle
|
|
150
|
+
["/thinking", "Cycle the selected model's supported reasoning efforts"],
|
|
151
151
|
["Ctrl+V", "Attach an image from the clipboard"],
|
|
152
152
|
["Ctrl+G", "Edit the composer in your external editor"],
|
|
153
153
|
["Ctrl+O", "Expand or collapse full tool output"],
|
|
@@ -232,7 +232,7 @@ export const workbenchCommands: SlashCommand[] = [
|
|
|
232
232
|
},
|
|
233
233
|
{
|
|
234
234
|
name: "thinking",
|
|
235
|
-
summary: "Cycle
|
|
235
|
+
summary: "Cycle reasoning effort using the selected model's supported levels",
|
|
236
236
|
run: (ctx) => {
|
|
237
237
|
ctx.conductor.cycleThinkingLevel();
|
|
238
238
|
return HANDLED;
|
package/src/index.ts
CHANGED
|
@@ -15,4 +15,5 @@ export * as addons from "./addons/index.js";
|
|
|
15
15
|
export * as channels from "./channels/index.js";
|
|
16
16
|
export * as runtimeBridge from "./runtime-bridge/index.js";
|
|
17
17
|
export * as insight from "./insight/index.js";
|
|
18
|
+
export * as reviewAgent from "./review-agent/index.js";
|
|
18
19
|
export { VERSION } from "./workspace/index.js";
|
package/src/launch/catalog.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { ModelRegistry, type KnownProvider, type Model } from "indusagi/ai";
|
|
5
5
|
import type { CatalogFilter } from "./contract.js";
|
|
6
|
+
import { applyCodingAgentModelOverlay } from "../models.js";
|
|
6
7
|
|
|
7
8
|
export interface CatalogIo {
|
|
8
9
|
print(line: string): void;
|
|
@@ -22,10 +23,11 @@ export interface CatalogModelSource {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export function registrySource(registry: ModelRegistry = new ModelRegistry()): CatalogModelSource {
|
|
26
|
+
const overlaid = applyCodingAgentModelOverlay(registry);
|
|
25
27
|
return {
|
|
26
|
-
providers: (): readonly (KnownProvider | string)[] =>
|
|
28
|
+
providers: (): readonly (KnownProvider | string)[] => overlaid.getProviders(),
|
|
27
29
|
models: (provider: string): readonly Model<Model<string>["api"]>[] =>
|
|
28
|
-
|
|
30
|
+
overlaid.getModels(provider),
|
|
29
31
|
};
|
|
30
32
|
}
|
|
31
33
|
|
package/src/launch/contract.ts
CHANGED
|
@@ -20,7 +20,7 @@ export const FLAG_GROUPS: readonly { readonly id: FlagGroup; readonly title: str
|
|
|
20
20
|
{ id: "meta", title: "Meta" },
|
|
21
21
|
];
|
|
22
22
|
|
|
23
|
-
const THINKING_CHOICES = ["off", "minimal", "low", "medium", "high", "xhigh"];
|
|
23
|
+
const THINKING_CHOICES = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
24
24
|
|
|
25
25
|
export const FLAG_SPECS: readonly GroupedFlagSpec[] = [
|
|
26
26
|
/* ---- Output ---------------------------------------------------------- */
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
isToolName,
|
|
10
10
|
} from "../contract.js";
|
|
11
11
|
import { FLAG_SPECS, type GroupedFlagSpec } from "./flags.js";
|
|
12
|
+
import { isReasoningEffortSupported } from "../../models.js";
|
|
12
13
|
|
|
13
14
|
interface ParseState {
|
|
14
15
|
flags: Record<string, FlagValue>;
|
|
@@ -154,14 +155,14 @@ function readBool(flags: Record<string, FlagValue>, key: string): boolean {
|
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
function deriveMode(flags: Record<string, FlagValue>): Invocation["mode"] {
|
|
157
|
-
if (readBool(flags, "json")) return "rpc";
|
|
158
158
|
if (readBool(flags, "print") && !readBool(flags, "interactive")) return "json";
|
|
159
|
+
if (readBool(flags, "json")) return "rpc";
|
|
159
160
|
return "text";
|
|
160
161
|
}
|
|
161
162
|
|
|
162
|
-
function resolveThinking(flags: Record<string, FlagValue
|
|
163
|
+
function resolveThinking(flags: Record<string, FlagValue>, modelId: string | undefined): Invocation["thinking"] {
|
|
163
164
|
const raw = readString(flags, "thinking");
|
|
164
|
-
return raw !== undefined && isThinkingEffort(raw) ? raw : undefined;
|
|
165
|
+
return raw !== undefined && isThinkingEffort(raw) && isReasoningEffortSupported(modelId, raw) ? raw : undefined;
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
function resolveTools(flags: Record<string, FlagValue>): Invocation["tools"] {
|
|
@@ -215,7 +216,7 @@ export function readInvocation(argv: readonly string[]): Invocation {
|
|
|
215
216
|
const cwd = readString(flags, "cwd");
|
|
216
217
|
const system = readString(flags, "system");
|
|
217
218
|
const appendSystem = readString(flags, "append-system");
|
|
218
|
-
const thinking = resolveThinking(flags);
|
|
219
|
+
const thinking = resolveThinking(flags, model);
|
|
219
220
|
const tools = resolveTools(flags);
|
|
220
221
|
return {
|
|
221
222
|
mode,
|
package/src/models.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { ModelRegistry, type Api, type Model } from "indusagi/ai";
|
|
2
|
+
|
|
3
|
+
export type AstraReasoningLevel = "low" | "medium" | "high" | "xhigh" | "max";
|
|
4
|
+
|
|
5
|
+
export interface LongContextPricing {
|
|
6
|
+
readonly threshold: number;
|
|
7
|
+
readonly multipliers: {
|
|
8
|
+
readonly input: number;
|
|
9
|
+
readonly cacheRead: number;
|
|
10
|
+
readonly cacheWrite: number;
|
|
11
|
+
readonly output: number;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface CodingAgentModel<TApi extends Api = Api> extends Model<TApi> {
|
|
16
|
+
readonly reasoningEfforts?: readonly AstraReasoningLevel[];
|
|
17
|
+
readonly longContextPricing?: LongContextPricing;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const GPT_6_ASTRA_BASE = {
|
|
21
|
+
id: "gpt-6-astra",
|
|
22
|
+
name: "GPT-6 Astra",
|
|
23
|
+
api: "openai-responses",
|
|
24
|
+
reasoning: true,
|
|
25
|
+
reasoningEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
26
|
+
input: ["text", "image"] as Array<"text" | "image">,
|
|
27
|
+
cost: {
|
|
28
|
+
input: 10,
|
|
29
|
+
cacheRead: 1,
|
|
30
|
+
cacheWrite: 12.5,
|
|
31
|
+
output: 50,
|
|
32
|
+
},
|
|
33
|
+
contextWindow: 1_050_000,
|
|
34
|
+
maxTokens: 128_000,
|
|
35
|
+
longContextPricing: {
|
|
36
|
+
threshold: 272_000,
|
|
37
|
+
multipliers: {
|
|
38
|
+
input: 2,
|
|
39
|
+
cacheRead: 2,
|
|
40
|
+
cacheWrite: 2,
|
|
41
|
+
output: 1.5,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
} as const;
|
|
45
|
+
|
|
46
|
+
export const CODING_AGENT_MODELS: readonly CodingAgentModel[] = [
|
|
47
|
+
{
|
|
48
|
+
...GPT_6_ASTRA_BASE,
|
|
49
|
+
provider: "openai",
|
|
50
|
+
baseUrl: "https://api.openai.com/v1",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
...GPT_6_ASTRA_BASE,
|
|
54
|
+
api: "openai-codex-responses",
|
|
55
|
+
provider: "openai-codex",
|
|
56
|
+
baseUrl: "https://chatgpt.com/backend-api",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
...GPT_6_ASTRA_BASE,
|
|
60
|
+
provider: "codex-cli",
|
|
61
|
+
baseUrl: "bridge:codex-cli",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
...GPT_6_ASTRA_BASE,
|
|
65
|
+
id: "openai/gpt-6-astra",
|
|
66
|
+
provider: "indusagi-cli",
|
|
67
|
+
baseUrl: "bridge:indusagi-cli",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
...GPT_6_ASTRA_BASE,
|
|
71
|
+
id: "openai-codex/gpt-6-astra",
|
|
72
|
+
api: "openai-codex-responses",
|
|
73
|
+
provider: "indusagi-cli",
|
|
74
|
+
baseUrl: "bridge:indusagi-cli",
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
export function applyCodingAgentModelOverlay(registry: ModelRegistry): ModelRegistry {
|
|
79
|
+
for (const model of CODING_AGENT_MODELS) registry.registerCustomModel(model);
|
|
80
|
+
return registry;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function createCodingAgentModelRegistry(): ModelRegistry {
|
|
84
|
+
return applyCodingAgentModelOverlay(new ModelRegistry());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function isReasoningEffortSupported(modelId: string | undefined, effort: string): boolean {
|
|
88
|
+
if (modelId?.split("/").at(-1) !== "gpt-6-astra") return true;
|
|
89
|
+
return GPT_6_ASTRA_BASE.reasoningEfforts.includes(effort as AstraReasoningLevel);
|
|
90
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const SDSDSD_SUPABASE_URL = "https://bnaadeovgcqgbrnrfcef.supabase.co";
|
|
2
|
+
|
|
3
|
+
export interface ReviewAgentConfig {
|
|
4
|
+
host: string;
|
|
5
|
+
port: number;
|
|
6
|
+
supabaseUrl: string;
|
|
7
|
+
supabaseServiceKey: string;
|
|
8
|
+
githubWebhookSecret: string;
|
|
9
|
+
githubToken?: string;
|
|
10
|
+
githubApp?: { appId: string; privateKey: string };
|
|
11
|
+
modelId: string;
|
|
12
|
+
pollIntervalMs: number;
|
|
13
|
+
maxJobsPerTick: number;
|
|
14
|
+
providerKeys: Readonly<Record<string, string>>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const PROVIDER_ENV: Record<string, string> = {
|
|
18
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
19
|
+
openai: "OPENAI_API_KEY",
|
|
20
|
+
google: "GEMINI_API_KEY",
|
|
21
|
+
minimax: "MINIMAX_API_KEY",
|
|
22
|
+
mistral: "MISTRAL_API_KEY",
|
|
23
|
+
xai: "XAI_API_KEY",
|
|
24
|
+
openrouter: "OPENROUTER_API_KEY",
|
|
25
|
+
groq: "GROQ_API_KEY",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function required(env: NodeJS.ProcessEnv, name: string): string {
|
|
29
|
+
const value = env[name]?.trim();
|
|
30
|
+
if (!value) throw new Error(`${name} is required`);
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function positiveInteger(value: string | undefined, fallback: number, name: string): number {
|
|
35
|
+
if (!value) return fallback;
|
|
36
|
+
const parsed = Number(value);
|
|
37
|
+
if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`${name} must be a positive integer`);
|
|
38
|
+
return parsed;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function loadReviewAgentConfig(env: NodeJS.ProcessEnv = process.env): ReviewAgentConfig {
|
|
42
|
+
const supabaseServiceKey = required(env, "SUPABASE_SERVICE_ROLE_KEY");
|
|
43
|
+
const githubWebhookSecret = required(env, "GITHUB_WEBHOOK_SECRET");
|
|
44
|
+
const githubToken = env.GITHUB_TOKEN?.trim();
|
|
45
|
+
const appId = env.GITHUB_APP_ID?.trim();
|
|
46
|
+
const privateKey = env.GITHUB_APP_PRIVATE_KEY?.replace(/\\n/g, "\n").trim();
|
|
47
|
+
if (!githubToken && !(appId && privateKey)) {
|
|
48
|
+
throw new Error("Configure GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY, or GITHUB_TOKEN for development");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const modelId = env.INDUSAGI_REVIEW_MODEL?.trim() || "anthropic/claude-sonnet-4-6";
|
|
52
|
+
const provider = modelId.split("/", 1)[0];
|
|
53
|
+
const providerEnv = PROVIDER_ENV[provider];
|
|
54
|
+
if (providerEnv && !env[providerEnv]?.trim()) throw new Error(`${providerEnv} is required for ${modelId}`);
|
|
55
|
+
|
|
56
|
+
const providerKeys: Record<string, string> = {};
|
|
57
|
+
for (const [id, name] of Object.entries(PROVIDER_ENV)) {
|
|
58
|
+
const value = env[name]?.trim();
|
|
59
|
+
if (value) providerKeys[id] = value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
host: env.REVIEW_AGENT_HOST?.trim() || env.HOST?.trim() || "0.0.0.0",
|
|
64
|
+
port: positiveInteger(env.REVIEW_AGENT_PORT ?? env.PORT, 8787, "REVIEW_AGENT_PORT"),
|
|
65
|
+
supabaseUrl: env.SUPABASE_URL?.trim().replace(/\/$/, "") || SDSDSD_SUPABASE_URL,
|
|
66
|
+
supabaseServiceKey,
|
|
67
|
+
githubWebhookSecret,
|
|
68
|
+
...(githubToken ? { githubToken } : {}),
|
|
69
|
+
...(appId && privateKey ? { githubApp: { appId, privateKey } } : {}),
|
|
70
|
+
modelId,
|
|
71
|
+
pollIntervalMs: positiveInteger(env.REVIEW_AGENT_POLL_MS ?? env.REVIEW_POLL_INTERVAL_MS, 2_000, "REVIEW_AGENT_POLL_MS"),
|
|
72
|
+
maxJobsPerTick: positiveInteger(env.REVIEW_AGENT_JOBS_PER_TICK ?? env.REVIEW_MAX_JOBS_PER_TICK, 5, "REVIEW_AGENT_JOBS_PER_TICK"),
|
|
73
|
+
providerKeys,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export const REVIEW_SEVERITIES = ["critical", "high", "medium", "low", "nit"] as const;
|
|
2
|
+
export const REVIEW_RISKS = ["low", "medium", "high"] as const;
|
|
3
|
+
|
|
4
|
+
export type ReviewSeverity = (typeof REVIEW_SEVERITIES)[number];
|
|
5
|
+
export type ReviewRisk = (typeof REVIEW_RISKS)[number];
|
|
6
|
+
export type ReviewSide = "LEFT" | "RIGHT";
|
|
7
|
+
|
|
8
|
+
export interface ChangedFile {
|
|
9
|
+
filename: string;
|
|
10
|
+
status: string;
|
|
11
|
+
additions: number;
|
|
12
|
+
deletions: number;
|
|
13
|
+
patch?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PullRequestContext {
|
|
17
|
+
repository: string;
|
|
18
|
+
pullNumber: number;
|
|
19
|
+
headSha: string;
|
|
20
|
+
baseSha?: string;
|
|
21
|
+
title: string;
|
|
22
|
+
description: string;
|
|
23
|
+
author: string;
|
|
24
|
+
additions: number;
|
|
25
|
+
deletions: number;
|
|
26
|
+
changedFiles: ChangedFile[];
|
|
27
|
+
diff: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ReviewFinding {
|
|
31
|
+
severity: ReviewSeverity;
|
|
32
|
+
confidence: number;
|
|
33
|
+
title: string;
|
|
34
|
+
path: string;
|
|
35
|
+
line: number;
|
|
36
|
+
side: ReviewSide;
|
|
37
|
+
body: string;
|
|
38
|
+
suggestion?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ReviewOutput {
|
|
42
|
+
summary: string;
|
|
43
|
+
risk: ReviewRisk;
|
|
44
|
+
findings: ReviewFinding[];
|
|
45
|
+
testPlan: string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ReviewRequest {
|
|
49
|
+
deliveryId: string;
|
|
50
|
+
action: string;
|
|
51
|
+
installationId: number;
|
|
52
|
+
repository: string;
|
|
53
|
+
owner: string;
|
|
54
|
+
repo: string;
|
|
55
|
+
pullNumber: number;
|
|
56
|
+
headSha: string;
|
|
57
|
+
title: string;
|
|
58
|
+
draft: boolean;
|
|
59
|
+
}
|