indusagi-coding-agent 0.2.11 → 0.2.12
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/CHANGELOG.md +6 -0
- package/dist/entry.js +2 -2
- package/dist/index.js +2 -2
- package/package.json +2 -5
- package/src/conductor/contract.ts +3 -0
- package/src/conductor/signal-hub/translate.ts +2 -2
- package/src/launch/invocation/read.ts +1 -1
- package/src/workspace/brand.ts +1 -1
- 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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.12]
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **One-shot NDJSON routing**: combining `--print --json` now runs one-shot mode with structured signal output instead of selecting the stdin-driven RPC mode.
|
|
7
|
+
- **Structured tool signal contract**: `tool_start` and `tool_end` types now include the arguments, result, and error fields already emitted at runtime.
|
|
8
|
+
|
|
3
9
|
## [0.2.11]
|
|
4
10
|
|
|
5
11
|
### Fixed
|
package/dist/entry.js
CHANGED
|
@@ -18,7 +18,7 @@ var BRAND = Object.freeze({
|
|
|
18
18
|
envShareViewer: "INDUSAGI_SHARE_VIEWER_URL",
|
|
19
19
|
shareViewerUrl: "https://buildwithindusagi.ai/session/"
|
|
20
20
|
});
|
|
21
|
-
var VERSION = "0.2.
|
|
21
|
+
var VERSION = "0.2.12";
|
|
22
22
|
|
|
23
23
|
// src/launch/credentials.ts
|
|
24
24
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
@@ -1441,8 +1441,8 @@ function readBool(flags, key) {
|
|
|
1441
1441
|
return flags[key] === true;
|
|
1442
1442
|
}
|
|
1443
1443
|
function deriveMode(flags) {
|
|
1444
|
-
if (readBool(flags, "json")) return "rpc";
|
|
1445
1444
|
if (readBool(flags, "print") && !readBool(flags, "interactive")) return "json";
|
|
1445
|
+
if (readBool(flags, "json")) return "rpc";
|
|
1446
1446
|
return "text";
|
|
1447
1447
|
}
|
|
1448
1448
|
function resolveThinking(flags) {
|
package/dist/index.js
CHANGED
|
@@ -50,7 +50,7 @@ var BRAND = Object.freeze({
|
|
|
50
50
|
envShareViewer: "INDUSAGI_SHARE_VIEWER_URL",
|
|
51
51
|
shareViewerUrl: "https://buildwithindusagi.ai/session/"
|
|
52
52
|
});
|
|
53
|
-
var VERSION = "0.2.
|
|
53
|
+
var VERSION = "0.2.12";
|
|
54
54
|
|
|
55
55
|
// src/launch/credentials.ts
|
|
56
56
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
@@ -1520,8 +1520,8 @@ function readBool(flags, key) {
|
|
|
1520
1520
|
return flags[key] === true;
|
|
1521
1521
|
}
|
|
1522
1522
|
function deriveMode(flags) {
|
|
1523
|
-
if (readBool(flags, "json")) return "rpc";
|
|
1524
1523
|
if (readBool(flags, "print") && !readBool(flags, "interactive")) return "json";
|
|
1524
|
+
if (readBool(flags, "json")) return "rpc";
|
|
1525
1525
|
return "text";
|
|
1526
1526
|
}
|
|
1527
1527
|
function resolveThinking(flags) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "indusagi-coding-agent",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
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,18 +22,15 @@
|
|
|
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
27
|
"indusagi": "dist/entry.js"
|
|
29
28
|
},
|
|
30
29
|
"exports": {
|
|
31
30
|
".": {
|
|
32
|
-
"types": "./dist/types/index.d.ts",
|
|
33
31
|
"import": "./dist/index.js"
|
|
34
32
|
},
|
|
35
33
|
"./guardrails": {
|
|
36
|
-
"types": "./dist/types/guardrails.d.ts",
|
|
37
34
|
"import": "./dist/guardrails.js"
|
|
38
35
|
},
|
|
39
36
|
"./package.json": "./package.json"
|
|
@@ -49,9 +46,9 @@
|
|
|
49
46
|
"scripts": {
|
|
50
47
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
51
48
|
"build": "node build.mjs",
|
|
52
|
-
"lineage-scan": "node scripts/lineage-scan.mjs",
|
|
53
49
|
"test": "vitest --run",
|
|
54
50
|
"test:watch": "vitest",
|
|
51
|
+
"prepublishOnly": "npm test && npm run build",
|
|
55
52
|
"start": "node dist/entry.js",
|
|
56
53
|
"start:help": "node dist/entry.js --help"
|
|
57
54
|
},
|
|
@@ -131,6 +131,7 @@ export type SessionSignal = {
|
|
|
131
131
|
readonly kind: "tool_start";
|
|
132
132
|
readonly id: string;
|
|
133
133
|
readonly name: string;
|
|
134
|
+
readonly args: unknown;
|
|
134
135
|
} | {
|
|
135
136
|
readonly kind: "tool_update";
|
|
136
137
|
readonly id: string;
|
|
@@ -140,6 +141,8 @@ export type SessionSignal = {
|
|
|
140
141
|
readonly kind: "tool_end";
|
|
141
142
|
readonly id: string;
|
|
142
143
|
readonly ok: boolean;
|
|
144
|
+
readonly result: unknown;
|
|
145
|
+
readonly isError: boolean;
|
|
143
146
|
} | {
|
|
144
147
|
readonly kind: "turn_end";
|
|
145
148
|
readonly usage: Usage;
|
|
@@ -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[] {
|
|
@@ -154,8 +154,8 @@ function readBool(flags: Record<string, FlagValue>, key: string): boolean {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
function deriveMode(flags: Record<string, FlagValue>): Invocation["mode"] {
|
|
157
|
-
if (readBool(flags, "json")) return "rpc";
|
|
158
157
|
if (readBool(flags, "print") && !readBool(flags, "interactive")) return "json";
|
|
158
|
+
if (readBool(flags, "json")) return "rpc";
|
|
159
159
|
return "text";
|
|
160
160
|
}
|
|
161
161
|
|
package/src/workspace/brand.ts
CHANGED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { translateAgentEvent } from "./translate.js";
|
|
3
|
-
|
|
4
|
-
type InputEvent = Parameters<typeof translateAgentEvent>[0];
|
|
5
|
-
|
|
6
|
-
describe("translateAgentEvent tool signals", () => {
|
|
7
|
-
it("includes tool arguments in tool_start", () => {
|
|
8
|
-
const event: InputEvent = {
|
|
9
|
-
type: "tool_execution_start",
|
|
10
|
-
toolCallId: "call-1",
|
|
11
|
-
toolName: "read",
|
|
12
|
-
args: { path: "README.md" },
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
expect(translateAgentEvent(event)).toEqual([{
|
|
16
|
-
kind: "tool_start",
|
|
17
|
-
id: "call-1",
|
|
18
|
-
name: "read",
|
|
19
|
-
args: { path: "README.md" },
|
|
20
|
-
}]);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("includes tool results and error status in tool_end", () => {
|
|
24
|
-
const event: InputEvent = {
|
|
25
|
-
type: "tool_execution_end",
|
|
26
|
-
toolCallId: "call-2",
|
|
27
|
-
toolName: "write",
|
|
28
|
-
result: { content: "permission denied" },
|
|
29
|
-
isError: true,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
expect(translateAgentEvent(event)).toEqual([{
|
|
33
|
-
kind: "tool_end",
|
|
34
|
-
id: "call-2",
|
|
35
|
-
ok: false,
|
|
36
|
-
result: { content: "permission denied" },
|
|
37
|
-
isError: true,
|
|
38
|
-
}]);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe("translateAgentEvent preserved signals", () => {
|
|
43
|
-
it("preserves prompt extraction", () => {
|
|
44
|
-
const event = {
|
|
45
|
-
type: "message_end",
|
|
46
|
-
message: {
|
|
47
|
-
role: "user",
|
|
48
|
-
content: [{ type: "text", text: "hello " }, { type: "text", text: "world" }],
|
|
49
|
-
timestamp: 1,
|
|
50
|
-
},
|
|
51
|
-
} as unknown as InputEvent;
|
|
52
|
-
|
|
53
|
-
expect(translateAgentEvent(event)).toEqual([{ kind: "prompt", text: "hello world" }]);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("preserves text and thinking deltas", () => {
|
|
57
|
-
const message = { role: "assistant", content: [], timestamp: 1 };
|
|
58
|
-
|
|
59
|
-
expect(translateAgentEvent({
|
|
60
|
-
type: "message_update",
|
|
61
|
-
message,
|
|
62
|
-
assistantMessageEvent: { type: "text_delta", delta: "answer" },
|
|
63
|
-
} as unknown as InputEvent)).toEqual([{ kind: "text", delta: "answer" }]);
|
|
64
|
-
expect(translateAgentEvent({
|
|
65
|
-
type: "message_update",
|
|
66
|
-
message,
|
|
67
|
-
assistantMessageEvent: { type: "thinking_delta", delta: "reason" },
|
|
68
|
-
} as unknown as InputEvent)).toEqual([{ kind: "thinking", delta: "reason" }]);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("preserves turn_end usage", () => {
|
|
72
|
-
const usage = { input: 1, output: 2, cacheRead: 3, cacheWrite: 4, totalTokens: 10, cost: { total: 0.5 } };
|
|
73
|
-
const event = {
|
|
74
|
-
type: "turn_end",
|
|
75
|
-
message: { role: "assistant", content: [], usage, timestamp: 1 },
|
|
76
|
-
toolResults: [],
|
|
77
|
-
} as unknown as InputEvent;
|
|
78
|
-
|
|
79
|
-
expect(translateAgentEvent(event)).toEqual([{ kind: "turn_end", usage }]);
|
|
80
|
-
});
|
|
81
|
-
});
|
package/src/entry.test.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { resolveBoot } from "./entry.js";
|
|
3
|
-
|
|
4
|
-
describe("resolveBoot", () => {
|
|
5
|
-
afterEach(() => vi.restoreAllMocks());
|
|
6
|
-
|
|
7
|
-
it("prints fatal boot errors and returns a failing exit code", async () => {
|
|
8
|
-
const stderr = vi.spyOn(process.stderr, "write").mockReturnValue(true);
|
|
9
|
-
|
|
10
|
-
await expect(resolveBoot(Promise.reject(new Error("startup failed")))).resolves.toBe(1);
|
|
11
|
-
expect(stderr).toHaveBeenCalledWith("indusagi: startup failed\n");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("preserves successful exit codes", async () => {
|
|
15
|
-
await expect(resolveBoot(Promise.resolve(0))).resolves.toBe(0);
|
|
16
|
-
});
|
|
17
|
-
});
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { resolveSarvamConfig, redactApiKey } from "./config.js";
|
|
3
|
-
|
|
4
|
-
describe("resolveSarvamConfig", () => {
|
|
5
|
-
const isolatedHome = "/tmp/indusagi-sarvam-test-home-empty";
|
|
6
|
-
|
|
7
|
-
it("returns undefined when nothing is configured", () => {
|
|
8
|
-
expect(
|
|
9
|
-
resolveSarvamConfig({ env: {}, cwd: "/tmp", home: isolatedHome, disabled: false }),
|
|
10
|
-
).toBeUndefined();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("prefers explicit key over env", () => {
|
|
14
|
-
const cfg = resolveSarvamConfig({
|
|
15
|
-
apiKey: "key-from-arg",
|
|
16
|
-
env: { SARVAM_API_KEY: "key-from-env" },
|
|
17
|
-
cwd: "/tmp",
|
|
18
|
-
home: isolatedHome,
|
|
19
|
-
});
|
|
20
|
-
expect(cfg?.apiKey).toBe("key-from-arg");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("honors SARVAM_API_KEY", () => {
|
|
24
|
-
const cfg = resolveSarvamConfig({
|
|
25
|
-
env: { SARVAM_API_KEY: "sk-test-123" },
|
|
26
|
-
cwd: "/tmp",
|
|
27
|
-
home: isolatedHome,
|
|
28
|
-
});
|
|
29
|
-
expect(cfg?.apiKey).toBe("sk-test-123");
|
|
30
|
-
expect(cfg?.id).toBe("sarvam");
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("disabled wins", () => {
|
|
34
|
-
const cfg = resolveSarvamConfig({
|
|
35
|
-
apiKey: "sk-test",
|
|
36
|
-
disabled: true,
|
|
37
|
-
cwd: "/tmp",
|
|
38
|
-
home: isolatedHome,
|
|
39
|
-
});
|
|
40
|
-
expect(cfg).toBeUndefined();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("honors basePath from options", () => {
|
|
44
|
-
const cfg = resolveSarvamConfig({
|
|
45
|
-
apiKey: "sk-test",
|
|
46
|
-
basePath: "/tmp/sarvam-out",
|
|
47
|
-
cwd: "/tmp",
|
|
48
|
-
home: isolatedHome,
|
|
49
|
-
});
|
|
50
|
-
expect(cfg?.basePath).toBe("/tmp/sarvam-out");
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
describe("redactApiKey", () => {
|
|
55
|
-
it("masks middle", () => {
|
|
56
|
-
const r = redactApiKey("abcdefghijklmnop");
|
|
57
|
-
expect(r).toContain("…");
|
|
58
|
-
expect(r).not.toBe("abcdefghijklmnop");
|
|
59
|
-
});
|
|
60
|
-
});
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { resolveZohoConfig, redactUrl } from "./config.js";
|
|
3
|
-
|
|
4
|
-
describe("resolveZohoConfig", () => {
|
|
5
|
-
const isolatedHome = "/tmp/indusagi-zoho-test-home-empty";
|
|
6
|
-
|
|
7
|
-
it("returns undefined when nothing is configured", () => {
|
|
8
|
-
expect(
|
|
9
|
-
resolveZohoConfig({ env: {}, cwd: "/tmp", home: isolatedHome, disabled: false }),
|
|
10
|
-
).toBeUndefined();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("prefers explicit url over env", () => {
|
|
14
|
-
const cfg = resolveZohoConfig({
|
|
15
|
-
url: "https://mcp.example/a",
|
|
16
|
-
env: { ZOHO_MCP_URL: "https://mcp.example/b" },
|
|
17
|
-
cwd: "/tmp",
|
|
18
|
-
home: isolatedHome,
|
|
19
|
-
});
|
|
20
|
-
expect(cfg?.url).toBe("https://mcp.example/a");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("honors ZOHO_MCP_URL", () => {
|
|
24
|
-
const cfg = resolveZohoConfig({
|
|
25
|
-
env: { ZOHO_MCP_URL: "https://mcp.zoho.example/x" },
|
|
26
|
-
cwd: "/tmp",
|
|
27
|
-
home: isolatedHome,
|
|
28
|
-
});
|
|
29
|
-
expect(cfg?.url).toBe("https://mcp.zoho.example/x");
|
|
30
|
-
expect(cfg?.id).toBe("zoho");
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("disabled wins", () => {
|
|
34
|
-
const cfg = resolveZohoConfig({
|
|
35
|
-
url: "https://mcp.example/a",
|
|
36
|
-
disabled: true,
|
|
37
|
-
cwd: "/tmp",
|
|
38
|
-
home: isolatedHome,
|
|
39
|
-
});
|
|
40
|
-
expect(cfg).toBeUndefined();
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe("redactUrl", () => {
|
|
45
|
-
it("keeps host", () => {
|
|
46
|
-
const r = redactUrl("https://mcp.zoho.in/secret/path/more");
|
|
47
|
-
expect(r).toContain("mcp.zoho.in");
|
|
48
|
-
expect(r).toContain("…");
|
|
49
|
-
});
|
|
50
|
-
});
|
package/src/launch/oauth.test.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { hasRegisteredOAuthClientId, launchAttempts } from "./oauth.js";
|
|
3
|
-
|
|
4
|
-
afterEach(() => vi.unstubAllEnvs());
|
|
5
|
-
|
|
6
|
-
describe("OpenAI Codex OAuth registration", () => {
|
|
7
|
-
it("uses the framework client when no environment override exists", () => {
|
|
8
|
-
vi.stubEnv("INDUSAGI_OPENAI_CODEX_CLIENT_ID", "");
|
|
9
|
-
expect(hasRegisteredOAuthClientId("openai-codex")).toBe(true);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it("opens ampersand-heavy OAuth URLs on Windows without cmd parsing", () => {
|
|
13
|
-
const url = "https://example.com/oauth?response_type=code&client_id=client&scope=openid";
|
|
14
|
-
expect(launchAttempts(url, "win32")).toEqual([
|
|
15
|
-
{ command: "rundll32.exe", args: ["url.dll,FileProtocolHandler", url] },
|
|
16
|
-
]);
|
|
17
|
-
});
|
|
18
|
-
});
|