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 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.11";
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.11";
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.11",
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 } as SessionSignal],
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
- } as SessionSignal],
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
 
@@ -26,4 +26,4 @@ export const BRAND: Brand = Object.freeze({
26
26
  /**
27
27
  * The product version — printed by `--version` and shown on the startup banner.
28
28
  */
29
- export const VERSION = "0.2.11";
29
+ export const VERSION = "0.2.12";
@@ -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
- });
@@ -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
- });