xcoder-jarvis-agent 1.3.0 → 1.4.0

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/README.md CHANGED
@@ -1,5 +1,5 @@
1
- <!-- xcoder:version 3 | xcoder:task task-482677 | xcoder:updated 2026-11-05T03:54:36.038Z | xcoder:subtask code-st-729fd8 -->
2
- # @xcoder/jarvis-agent
1
+ <!-- xcoder:version 7 | xcoder:task task-691585 | xcoder:updated 2026-11-05T05:50:24.571Z | xcoder:subtask code-st-b5251b -->
2
+ # xcoder-jarvis-agent
3
3
 
4
4
  Jarvis is a ReAct agent that runs as a local CLI inside a developer's
5
5
  workspace. It reasons in a bounded loop, reads the workspace through a small
@@ -12,12 +12,31 @@ TypeScript, npm workspaces, vitest.
12
12
  ## Install and run
13
13
 
14
14
  ```bash
15
- npm run build -w @xcoder/jarvis-agent
15
+ npm run build -w xcoder-jarvis-agent
16
16
  node packages/jarvis-agent/dist/program.js "summarise how the llm client picks a transport"
17
17
  ```
18
18
 
19
19
  Once published, the binaries are `jarvis`.
20
20
 
21
+ ### Reaching jarvis through `xcoder`
22
+
23
+ `xcoder` forwards a `jarvis` command to this binary, so you do not have to
24
+ leave it or remember a dist path:
25
+
26
+ ```bash
27
+ xcoder jarvis --chat
28
+ ```
29
+
30
+ Every argument is handed over verbatim, stdio is inherited (a chat session
31
+ keeps your terminal), and the exit code is jarvis's own — `xcoder` adds only
32
+ `127` when the binary cannot be found and `130` when the run was interrupted.
33
+ No jarvis flag is re-documented there: run `xcoder jarvis --help` for the
34
+ authoritative list. `jarvis` is one of only three delegable targets, alongside
35
+ `litebro` and `bugfinder`; anything else is refused. Jarvis keeps its own
36
+ environment variables, kill switch and run budget — delegation adds none.
37
+ See [DELEGATION.md](https://github.com/org/xcoder-sdlc/blob/main/docs/DELEGATION.md)
38
+ and [LICENSE](./LICENSE).
39
+
21
40
  ### Flags
22
41
 
23
42
  | Flag | Meaning | Default |
@@ -215,7 +234,7 @@ script you were about to run.
215
234
  ## Development
216
235
 
217
236
  ```bash
218
- npm run typecheck -w @xcoder/jarvis-agent
237
+ npm run typecheck -w xcoder-jarvis-agent
219
238
  npm test # full workspace suite
220
239
  npx vitest run packages/jarvis-agent # just this package
221
240
  npm run check:boundaries # dependency rules
@@ -4,14 +4,7 @@ import { type JarvisOutcome } from "./telemetry/provenance.js";
4
4
  import { type ConfirmFn } from "./util/confirm.js";
5
5
  import { type ToolDescriptor } from "./tools/registry.js";
6
6
  export declare const JARVIS_AGENT_NAME: "jarvis-agent";
7
- /** Exit codes are the CLI contract (design §2.3, tradeoff T7). */
8
- export declare const EXIT: {
9
- readonly answered: 0;
10
- readonly error: 1;
11
- readonly iteration_limit: 2;
12
- readonly budget_exceeded: 3;
13
- readonly aborted: 4;
14
- };
7
+ export { EXIT, type ExitCode } from "./exit.js";
15
8
  export interface JarvisDeps {
16
9
  createClient?: () => LlmCompleter;
17
10
  now?: () => Date;
@@ -1,4 +1,4 @@
1
- // xcoder:version 13 | xcoder:task task-482677 | xcoder:updated 2026-11-05T03:48:53.483Z | xcoder:subtask code-st-729fd8
1
+ // xcoder:version 16 | xcoder:task task-482677 | xcoder:updated 2026-11-05T03:51:32.763Z | xcoder:subtask code-st-729fd8
2
2
  import fsp from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { createLlmClient, requestScope, shortId, XCODER_VERSION } from "xcoder-dna";
@@ -6,6 +6,7 @@ import { ArgvError, parseArgv, USAGE } from "./argv.js";
6
6
  import { BudgetExceeded, clampIterations, DEFAULT_CAPS, RunBudget } from "./budget.js";
7
7
  import { CHAT_DIRECTIVE } from "./chat/prompt.js";
8
8
  import { runChatSession } from "./chat/session.js";
9
+ import { EXIT } from "./exit.js";
9
10
  import { MemoryStore, resolveUserId, workspaceScopeId } from "./memory/store.js";
10
11
  import { assembleSystemPrompt } from "./prompt/assemble.js";
11
12
  import { POLICY_VERSION, renderPolicy } from "./prompt/policy.js";
@@ -19,14 +20,9 @@ import { DEFAULT_LIMITS, createToolRegistry } from "./tools/registry.js";
19
20
  import { FS_TOOLS } from "./tools/fs-tools.js";
20
21
  import { MEMORY_TOOLS } from "./tools/memory-tools.js";
21
22
  export const JARVIS_AGENT_NAME = "jarvis-agent";
22
- /** Exit codes are the CLI contract (design §2.3, tradeoff T7). */
23
- export const EXIT = {
24
- answered: 0,
25
- error: 1,
26
- iteration_limit: 2,
27
- budget_exceeded: 3,
28
- aborted: 4,
29
- };
23
+ // Re-exported so `EXIT` keeps its documented public path (design §2.3, T7),
24
+ // even though the table itself now lives in a leaf module.
25
+ export { EXIT } from "./exit.js";
30
26
  function emit(stream, text) {
31
27
  try {
32
28
  stream.write(text);
@@ -70,8 +66,11 @@ export async function runJarvisAgent(argv, deps = {}) {
70
66
  return EXIT.answered;
71
67
  }
72
68
  // Chat runs many turns over one stdin/stdout pair; it owns its own loop.
69
+ // `runJarvisAgent` is injected rather than imported by the loop, so the two
70
+ // modules stay a DAG instead of a cycle (the loop calls back into this
71
+ // function once per turn, inheriting the same validation gate).
73
72
  if (request.chat) {
74
- return await runChatSession(request, deps);
73
+ return await runChatSession(request, runJarvisAgent, deps);
75
74
  }
76
75
  if (request.task.length === 0) {
77
76
  emit(stderr, `jarvis: no task given\n\n${USAGE}\n`);
@@ -1,4 +1,4 @@
1
- import { type JarvisDeps } from "../agent.js";
1
+ import type { JarvisDeps } from "../agent.js";
2
2
  import type { JarvisRunRequest } from "../argv.js";
3
3
  import { CHAT_DIRECTIVE } from "./prompt.js";
4
4
  /**
@@ -21,6 +21,14 @@ import { CHAT_DIRECTIVE } from "./prompt.js";
21
21
  * session (K6/AC16/AC17).
22
22
  */
23
23
  export type StopReason = "terminator" | "eof" | "max_turns" | "killed";
24
+ /**
25
+ * Executes one single-shot run and returns its exit code.
26
+ *
27
+ * Injected rather than imported: `agent.ts` imports this module, so importing
28
+ * `agent.ts` back would make the two a runtime cycle. Inverting the dependency
29
+ * keeps the graph a DAG and makes the loop trivially fakeable in a test.
30
+ */
31
+ export type AgentRunner = (argv: readonly string[], deps: JarvisDeps) => Promise<number>;
24
32
  export interface ChatResult {
25
33
  exitCode: number;
26
34
  stopReason: StopReason;
@@ -32,7 +40,7 @@ export interface ChatResult {
32
40
  * gate instead of a second, divergent parser.
33
41
  */
34
42
  export declare function turnArgv(request: JarvisRunRequest, session: string, text: string): string[];
35
- export declare function runChatSession(request: JarvisRunRequest, deps?: JarvisDeps): Promise<number>;
36
- export declare function runChatSessionDetailed(request: JarvisRunRequest, deps?: JarvisDeps): Promise<ChatResult>;
43
+ export declare function runChatSession(request: JarvisRunRequest, runAgent: AgentRunner, deps?: JarvisDeps): Promise<number>;
44
+ export declare function runChatSessionDetailed(request: JarvisRunRequest, runAgent: AgentRunner, deps?: JarvisDeps): Promise<ChatResult>;
37
45
  /** Re-exported so a prompt test can pin the constant directive (AC15). */
38
46
  export { CHAT_DIRECTIVE };
@@ -1,5 +1,4 @@
1
- // xcoder:version 2 | xcoder:task task-482677 | xcoder:updated 2026-11-05T03:48:55.859Z | xcoder:subtask code-st-729fd8
2
- import { EXIT, runJarvisAgent } from "../agent.js";
1
+ import { EXIT } from "../exit.js";
3
2
  import { readKillState } from "../util/kill.js";
4
3
  import { createChatConfirm } from "./confirm.js";
5
4
  import { createStdinLineSource } from "./lines.js";
@@ -27,18 +26,18 @@ export function turnArgv(request, session, text) {
27
26
  argv.push("--", text);
28
27
  return argv;
29
28
  }
30
- export async function runChatSession(request, deps = {}) {
29
+ export async function runChatSession(request, runAgent, deps = {}) {
31
30
  const stderr = deps.stderr ?? process.stderr;
32
31
  const workspaceRoot = deps.workspaceRoot ?? process.cwd();
33
- const result = await driveChat(request, deps, workspaceRoot, stderr);
32
+ const result = await driveChat(request, runAgent, deps, workspaceRoot, stderr);
34
33
  return result.exitCode;
35
34
  }
36
- export async function runChatSessionDetailed(request, deps = {}) {
35
+ export async function runChatSessionDetailed(request, runAgent, deps = {}) {
37
36
  const stderr = deps.stderr ?? process.stderr;
38
37
  const workspaceRoot = deps.workspaceRoot ?? process.cwd();
39
- return await driveChat(request, deps, workspaceRoot, stderr);
38
+ return await driveChat(request, runAgent, deps, workspaceRoot, stderr);
40
39
  }
41
- async function driveChat(request, deps, workspaceRoot, stderr) {
40
+ async function driveChat(request, runAgent, deps, workspaceRoot, stderr) {
42
41
  // A stable session label is what makes the turns one conversation.
43
42
  const session = request.session ?? `jarvis-chat-${Date.now().toString(36)}`;
44
43
  const lines = deps.lines ?? createStdinLineSource({ stdin: deps.stdin });
@@ -90,7 +89,7 @@ async function driveChat(request, deps, workspaceRoot, stderr) {
90
89
  if (isTerminator(next.text)) {
91
90
  return { exitCode: EXIT.answered, stopReason: "terminator", turns };
92
91
  }
93
- const code = await runJarvisAgent(turnArgv(request, session, next.text), turnDeps);
92
+ const code = await runAgent(turnArgv(request, session, next.text), turnDeps);
94
93
  turns += 1;
95
94
  if (code === EXIT.aborted) {
96
95
  return { exitCode: EXIT.aborted, stopReason: "killed", turns };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The CLI exit-code contract (design 2.3, tradeoff T7).
3
+ *
4
+ * These live in their own leaf module so the chat loop can name a code without
5
+ * importing `agent.ts`, which itself imports the chat loop. That keeps the
6
+ * module graph a DAG: a cycle would work under ESM only by accident of
7
+ * evaluation order and would break the first time a value were read at module
8
+ * scope.
9
+ */
10
+ export declare const EXIT: {
11
+ readonly answered: 0;
12
+ readonly error: 1;
13
+ readonly iteration_limit: 2;
14
+ readonly budget_exceeded: 3;
15
+ readonly aborted: 4;
16
+ };
17
+ export type ExitCode = (typeof EXIT)[keyof typeof EXIT];
@@ -0,0 +1,17 @@
1
+ // xcoder:version 1 | xcoder:task task-482677 | xcoder:updated 2026-11-05T03:51:06.005Z | xcoder:subtask code-st-729fd8
2
+ /**
3
+ * The CLI exit-code contract (design 2.3, tradeoff T7).
4
+ *
5
+ * These live in their own leaf module so the chat loop can name a code without
6
+ * importing `agent.ts`, which itself imports the chat loop. That keeps the
7
+ * module graph a DAG: a cycle would work under ESM only by accident of
8
+ * evaluation order and would break the first time a value were read at module
9
+ * scope.
10
+ */
11
+ export const EXIT = {
12
+ answered: 0,
13
+ error: 1,
14
+ iteration_limit: 2,
15
+ budget_exceeded: 3,
16
+ aborted: 4,
17
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xcoder-jarvis-agent",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Jarvis — a ReAct agent with scoped memory, skill-pack prompt assembly and a code-enforced tool allowlist, built on xcoder-dna.",
5
5
  "license": "MIT",
6
6
  "author": "Neural Node Labs",
@@ -32,7 +32,7 @@
32
32
  "test": "vitest run"
33
33
  },
34
34
  "dependencies": {
35
- "xcoder-dna": "^1.3.0",
35
+ "xcoder-dna": "^1.4.0",
36
36
  "zod": "^4.4.3"
37
37
  }
38
38
  }