@parall/claude-agent 1.22.0 → 1.24.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.
@@ -10,7 +10,7 @@ export declare function buildSpawnEnv(parentEnv: NodeJS.ProcessEnv, claudeHome:
10
10
  export declare class ClaudeCodeAdapter implements DispatchAdapter {
11
11
  private readonly opts;
12
12
  constructor(opts: ClaudeCodeAdapterOptions);
13
- dispatch({ bodyForAgent, sessionKey, context }: DispatchOpts): AsyncIterable<RuntimeEvent>;
13
+ dispatch({ event, bodyForAgent, sessionKey, context }: DispatchOpts): AsyncIterable<RuntimeEvent>;
14
14
  forkSession({ sessionKey }: ForkOpts): import("@parall/agent-core").ForkSessionHandle | null;
15
15
  cleanupFork({ fork }: CleanupForkOpts): void;
16
16
  private buildArgs;
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,KAAK,wBAAwB,GAAG,IAAI,CAClC,iBAAiB,EACf,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,oBAAoB,GACpB,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,OAAO,GACP,gBAAgB,GAChB,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,oBAAoB,CAAC;CACtC,CAAC;AAEF,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,CAAC,UAAU,EAC5B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,EAChC,IAAI,EAAE;IAAE,WAAW,EAAE,OAAO,CAAA;CAAE,GAC7B,MAAM,CAAC,UAAU,CAuBnB;AAED,qBAAa,iBAAkB,YAAW,eAAe;IAC3C,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,wBAAwB;IAEpD,QAAQ,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IA+EjG,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,QAAQ;IAIpC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAIrC,OAAO,CAAC,SAAS;CAkClB"}
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,KAAK,wBAAwB,GAAG,IAAI,CAClC,iBAAiB,EACf,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,oBAAoB,GACpB,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,OAAO,GACP,gBAAgB,GAChB,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,oBAAoB,CAAC;CACtC,CAAC;AAEF,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,CAAC,UAAU,EAC5B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,EAChC,IAAI,EAAE;IAAE,WAAW,EAAE,OAAO,CAAA;CAAE,GAC7B,MAAM,CAAC,UAAU,CAuBnB;AAED,qBAAa,iBAAkB,YAAW,eAAe;IAC3C,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,wBAAwB;IAEpD,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IAkGxG,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,QAAQ;IAIpC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAIrC,OAAO,CAAC,SAAS;CAkClB"}
package/dist/dispatch.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { spawn } from "node:child_process";
3
+ import { appendPreparedLocalAttachmentRefs, pinLocalAttachmentPaths, } from "@parall/agent-core/internal/attachment-input";
3
4
  import { parseClaudeStreamJson } from "./output-parser.js";
4
5
  export function buildSpawnEnv(parentEnv, claudeHome, context, opts) {
5
6
  const env = { ...parentEnv };
@@ -30,73 +31,91 @@ export class ClaudeCodeAdapter {
30
31
  constructor(opts) {
31
32
  this.opts = opts;
32
33
  }
33
- async *dispatch({ bodyForAgent, sessionKey, context }) {
34
- const args = this.buildArgs(sessionKey, bodyForAgent);
35
- const env = buildSpawnEnv(process.env, this.opts.claudeHome, context, {
36
- allowApiKey: this.opts.allowApiKey,
37
- });
38
- context.log?.info(`claude-agent[${context.accountId}]: spawn ${this.opts.claudeBin} ${args.slice(0, -1).join(" ")}`);
39
- const proc = spawn(this.opts.claudeBin, args, {
40
- cwd: this.opts.workspaceDir,
41
- env,
42
- stdio: ["ignore", "pipe", "pipe"],
43
- });
44
- if (!proc.stdout || !proc.stderr) {
45
- throw new Error("Claude subprocess did not provide stdio pipes");
34
+ async *dispatch({ event, bodyForAgent, sessionKey, context }) {
35
+ let promptBody = bodyForAgent;
36
+ let releasePreparedAttachments = () => { };
37
+ try {
38
+ const prepared = await appendPreparedLocalAttachmentRefs(bodyForAgent, event, context, {
39
+ workspaceDir: this.opts.workspaceDir,
40
+ log: context.log,
41
+ });
42
+ promptBody = prepared.body;
43
+ releasePreparedAttachments = pinLocalAttachmentPaths(prepared.attachments.images);
44
+ }
45
+ catch (err) {
46
+ context.log?.warn?.(`claude-agent[${context.accountId}]: failed to prepare local attachments: ${String(err)}`);
46
47
  }
47
- const stderr = [];
48
- proc.stderr.on("data", (chunk) => {
49
- stderr.push(chunk.toString());
50
- });
51
- const exitPromise = new Promise((resolve, reject) => {
52
- proc.once("error", reject);
53
- proc.once("close", (code, signal) => resolve({ code, signal }));
54
- });
55
- const groupKey = randomUUID();
56
- let sawError = false;
57
- let completed = false;
58
48
  try {
59
- for await (const event of parseClaudeStreamJson(proc.stdout)) {
60
- if (event.type === "session_id") {
61
- this.opts.sessionManager.recordSessionId(sessionKey, event.sessionId);
62
- continue;
49
+ const args = this.buildArgs(sessionKey, promptBody);
50
+ const env = buildSpawnEnv(process.env, this.opts.claudeHome, context, {
51
+ allowApiKey: this.opts.allowApiKey,
52
+ });
53
+ context.log?.info(`claude-agent[${context.accountId}]: spawn ${this.opts.claudeBin} ${args.slice(0, -1).join(" ")}`);
54
+ const proc = spawn(this.opts.claudeBin, args, {
55
+ cwd: this.opts.workspaceDir,
56
+ env,
57
+ stdio: ["ignore", "pipe", "pipe"],
58
+ });
59
+ if (!proc.stdout || !proc.stderr) {
60
+ throw new Error("Claude subprocess did not provide stdio pipes");
61
+ }
62
+ const stderr = [];
63
+ proc.stderr.on("data", (chunk) => {
64
+ stderr.push(chunk.toString());
65
+ });
66
+ const exitPromise = new Promise((resolve, reject) => {
67
+ proc.once("error", reject);
68
+ proc.once("close", (code, signal) => resolve({ code, signal }));
69
+ });
70
+ const groupKey = randomUUID();
71
+ let sawError = false;
72
+ let completed = false;
73
+ try {
74
+ for await (const streamEvent of parseClaudeStreamJson(proc.stdout)) {
75
+ if (streamEvent.type === "session_id") {
76
+ this.opts.sessionManager.recordSessionId(sessionKey, streamEvent.sessionId);
77
+ continue;
78
+ }
79
+ if (streamEvent.type === "error") {
80
+ sawError = true;
81
+ yield streamEvent;
82
+ continue;
83
+ }
84
+ if (streamEvent.type === "text") {
85
+ // Runtime output contract (symmetric with OpenClaw channel): Claude's
86
+ // plain text is never projected as a chat message. To reply, the agent
87
+ // must explicitly run `parall messages send` / `dm` via Bash. Text
88
+ // events are still recorded as suppressed session steps for audit.
89
+ yield {
90
+ ...streamEvent,
91
+ project: false,
92
+ groupKey,
93
+ };
94
+ continue;
95
+ }
96
+ yield {
97
+ ...streamEvent,
98
+ groupKey,
99
+ };
63
100
  }
64
- if (event.type === "error") {
65
- sawError = true;
66
- yield event;
67
- continue;
101
+ completed = true;
102
+ }
103
+ finally {
104
+ if (!completed && proc.exitCode === null && proc.signalCode === null) {
105
+ proc.kill("SIGTERM");
68
106
  }
69
- if (event.type === "text") {
70
- // Runtime output contract (symmetric with OpenClaw channel): Claude's
71
- // plain text is never projected as a chat message. To reply, the agent
72
- // must explicitly run `parall messages send` / `dm` via Bash. Text
73
- // events are still recorded as suppressed session steps for audit.
107
+ const { code, signal } = await exitPromise;
108
+ if ((code ?? 0) !== 0 && !sawError) {
109
+ const detail = stderr.join("").trim();
74
110
  yield {
75
- ...event,
76
- project: false,
77
- groupKey,
111
+ type: "error",
112
+ message: detail || `Claude exited with code ${code ?? "unknown"}${signal ? ` (${signal})` : ""}`,
78
113
  };
79
- continue;
80
114
  }
81
- yield {
82
- ...event,
83
- groupKey,
84
- };
85
115
  }
86
- completed = true;
87
116
  }
88
117
  finally {
89
- if (!completed && proc.exitCode === null && proc.signalCode === null) {
90
- proc.kill("SIGTERM");
91
- }
92
- const { code, signal } = await exitPromise;
93
- if ((code ?? 0) !== 0 && !sawError) {
94
- const detail = stderr.join("").trim();
95
- yield {
96
- type: "error",
97
- message: detail || `Claude exited with code ${code ?? "unknown"}${signal ? ` (${signal})` : ""}`,
98
- };
99
- }
118
+ releasePreparedAttachments();
100
119
  }
101
120
  }
102
121
  forkSession({ sessionKey }) {
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import * as os from "node:os";
3
3
  import { ParallAgentGateway, parseShutdownDeadlineMs } from "@parall/agent-core";
4
- import { ParallClient, ParallWs } from "@parall/sdk";
4
+ import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
5
5
  import { buildClaudeRuntimeKey, resolveClaudeAgentConfig, resolveWsUrl, sessionStateFilePathForRuntime, stepIdFilePathForSession, } from "./config.js";
6
6
  import { ClaudeCodeAdapter } from "./dispatch.js";
7
7
  import { ClaudeSessionManager } from "./session-manager.js";
@@ -13,6 +13,18 @@ function createLogger(prefix) {
13
13
  error: (msg) => console.error(`[${prefix}] ${msg}`),
14
14
  };
15
15
  }
16
+ async function getAgentMeWithLegacyFallback(client, orgId) {
17
+ try {
18
+ return await client.getAgentMe(orgId);
19
+ }
20
+ catch (err) {
21
+ if (!(err instanceof ApiError && err.status === 404)) {
22
+ throw err;
23
+ }
24
+ const user = await client.getMe();
25
+ return { ...user, agent_profile: null };
26
+ }
27
+ }
16
28
  async function main() {
17
29
  const config = resolveClaudeAgentConfig(process.env);
18
30
  const log = createLogger("claude-agent");
@@ -28,9 +40,13 @@ async function main() {
28
40
  token: config.apiKey,
29
41
  swimlaneName: config.swimlaneName,
30
42
  });
31
- const me = await client.getMe();
43
+ const me = await getAgentMeWithLegacyFallback(client, config.orgId);
32
44
  const agentUserId = me.id;
33
- ensureClaudeWorkspace(config.workspaceDir, log, { userId: agentUserId, displayName: me.display_name });
45
+ ensureClaudeWorkspace(config.workspaceDir, log, {
46
+ userId: agentUserId,
47
+ displayName: me.display_name,
48
+ description: me.agent_profile?.description ?? undefined,
49
+ });
34
50
  const runtimeKey = config.runtimeKey || buildClaudeRuntimeKey(agentUserId);
35
51
  const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
36
52
  const sessionManager = new ClaudeSessionManager(runtimeKey, sessionStateFilePath, log);
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,GAAG,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,EACrC,aAAa,CAAC,EAAE,aAAa,QAgC9B"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,GAAG,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,EACrC,aAAa,CAAC,EAAE,aAAa,QAiC9B"}
package/dist/workspace.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { BRIDGE_WORKSPACE_INSTRUCTIONS, PRLL_BEHAVIOR, PRLL_REFERENCE_GUIDE, buildIdentity, } from "@parall/agent-core";
4
+ import { ensureLocalAttachmentGitExclude } from "@parall/agent-core/internal/attachment-input";
4
5
  export function ensureClaudeWorkspace(workspaceDir, log, agentIdentity) {
5
6
  const CLAUDE_MD = [
6
7
  buildIdentity(agentIdentity),
@@ -30,4 +31,5 @@ export function ensureClaudeWorkspace(workspaceDir, log, agentIdentity) {
30
31
  }
31
32
  }
32
33
  fs.writeFileSync(claudeMdPath, CLAUDE_MD, "utf8");
34
+ ensureLocalAttachmentGitExclude(workspaceDir);
33
35
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/claude-agent",
3
- "version": "1.22.0",
3
+ "version": "1.24.0",
4
4
  "description": "Claude Code bridge runtime for self-hosted Parall agents",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,9 +25,9 @@
25
25
  "src"
26
26
  ],
27
27
  "dependencies": {
28
- "@parall/cli": "1.22.0",
29
- "@parall/sdk": "1.22.0",
30
- "@parall/agent-core": "1.22.0"
28
+ "@parall/agent-core": "1.24.0",
29
+ "@parall/cli": "1.24.0",
30
+ "@parall/sdk": "1.24.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^22.0.0",
package/src/dispatch.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { spawn } from "node:child_process";
3
+ import {
4
+ appendPreparedLocalAttachmentRefs,
5
+ pinLocalAttachmentPaths,
6
+ } from "@parall/agent-core/internal/attachment-input";
3
7
  import type {
4
8
  CleanupForkOpts,
5
9
  DispatchAdapter,
@@ -60,82 +64,101 @@ export function buildSpawnEnv(
60
64
  export class ClaudeCodeAdapter implements DispatchAdapter {
61
65
  constructor(private readonly opts: ClaudeCodeAdapterOptions) {}
62
66
 
63
- async *dispatch({ bodyForAgent, sessionKey, context }: DispatchOpts): AsyncIterable<RuntimeEvent> {
64
- const args = this.buildArgs(sessionKey, bodyForAgent);
65
- const env = buildSpawnEnv(process.env, this.opts.claudeHome, context, {
66
- allowApiKey: this.opts.allowApiKey,
67
- });
68
-
69
- context.log?.info(`claude-agent[${context.accountId}]: spawn ${this.opts.claudeBin} ${args.slice(0, -1).join(" ")}`);
70
-
71
- const proc = spawn(this.opts.claudeBin, args, {
72
- cwd: this.opts.workspaceDir,
73
- env,
74
- stdio: ["ignore", "pipe", "pipe"],
75
- });
76
-
77
- if (!proc.stdout || !proc.stderr) {
78
- throw new Error("Claude subprocess did not provide stdio pipes");
67
+ async *dispatch({ event, bodyForAgent, sessionKey, context }: DispatchOpts): AsyncIterable<RuntimeEvent> {
68
+ let promptBody = bodyForAgent;
69
+ let releasePreparedAttachments = () => {};
70
+ try {
71
+ const prepared = await appendPreparedLocalAttachmentRefs(bodyForAgent, event, context, {
72
+ workspaceDir: this.opts.workspaceDir,
73
+ log: context.log,
74
+ });
75
+ promptBody = prepared.body;
76
+ releasePreparedAttachments = pinLocalAttachmentPaths(prepared.attachments.images);
77
+ } catch (err) {
78
+ context.log?.warn?.(
79
+ `claude-agent[${context.accountId}]: failed to prepare local attachments: ${String(err)}`,
80
+ );
79
81
  }
80
82
 
81
- const stderr: string[] = [];
82
- proc.stderr.on("data", (chunk) => {
83
- stderr.push(chunk.toString());
84
- });
83
+ try {
84
+ const args = this.buildArgs(sessionKey, promptBody);
85
+ const env = buildSpawnEnv(process.env, this.opts.claudeHome, context, {
86
+ allowApiKey: this.opts.allowApiKey,
87
+ });
85
88
 
86
- const exitPromise = new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve, reject) => {
87
- proc.once("error", reject);
88
- proc.once("close", (code, signal) => resolve({ code, signal }));
89
- });
89
+ context.log?.info(`claude-agent[${context.accountId}]: spawn ${this.opts.claudeBin} ${args.slice(0, -1).join(" ")}`);
90
90
 
91
- const groupKey = randomUUID();
92
- let sawError = false;
93
- let completed = false;
91
+ const proc = spawn(this.opts.claudeBin, args, {
92
+ cwd: this.opts.workspaceDir,
93
+ env,
94
+ stdio: ["ignore", "pipe", "pipe"],
95
+ });
94
96
 
95
- try {
96
- for await (const event of parseClaudeStreamJson(proc.stdout)) {
97
- if (event.type === "session_id") {
98
- this.opts.sessionManager.recordSessionId(sessionKey, event.sessionId);
99
- continue;
100
- }
97
+ if (!proc.stdout || !proc.stderr) {
98
+ throw new Error("Claude subprocess did not provide stdio pipes");
99
+ }
101
100
 
102
- if (event.type === "error") {
103
- sawError = true;
104
- yield event;
105
- continue;
106
- }
101
+ const stderr: string[] = [];
102
+ proc.stderr.on("data", (chunk) => {
103
+ stderr.push(chunk.toString());
104
+ });
105
+
106
+ const exitPromise = new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve, reject) => {
107
+ proc.once("error", reject);
108
+ proc.once("close", (code, signal) => resolve({ code, signal }));
109
+ });
110
+
111
+ const groupKey = randomUUID();
112
+ let sawError = false;
113
+ let completed = false;
114
+
115
+ try {
116
+ for await (const streamEvent of parseClaudeStreamJson(proc.stdout)) {
117
+ if (streamEvent.type === "session_id") {
118
+ this.opts.sessionManager.recordSessionId(sessionKey, streamEvent.sessionId);
119
+ continue;
120
+ }
121
+
122
+ if (streamEvent.type === "error") {
123
+ sawError = true;
124
+ yield streamEvent;
125
+ continue;
126
+ }
127
+
128
+ if (streamEvent.type === "text") {
129
+ // Runtime output contract (symmetric with OpenClaw channel): Claude's
130
+ // plain text is never projected as a chat message. To reply, the agent
131
+ // must explicitly run `parall messages send` / `dm` via Bash. Text
132
+ // events are still recorded as suppressed session steps for audit.
133
+ yield {
134
+ ...streamEvent,
135
+ project: false,
136
+ groupKey,
137
+ };
138
+ continue;
139
+ }
107
140
 
108
- if (event.type === "text") {
109
- // Runtime output contract (symmetric with OpenClaw channel): Claude's
110
- // plain text is never projected as a chat message. To reply, the agent
111
- // must explicitly run `parall messages send` / `dm` via Bash. Text
112
- // events are still recorded as suppressed session steps for audit.
113
141
  yield {
114
- ...event,
115
- project: false,
142
+ ...streamEvent,
116
143
  groupKey,
117
144
  };
118
- continue;
119
145
  }
120
-
121
- yield {
122
- ...event,
123
- groupKey,
124
- };
146
+ completed = true;
147
+ } finally {
148
+ if (!completed && proc.exitCode === null && proc.signalCode === null) {
149
+ proc.kill("SIGTERM");
150
+ }
151
+ const { code, signal } = await exitPromise;
152
+ if ((code ?? 0) !== 0 && !sawError) {
153
+ const detail = stderr.join("").trim();
154
+ yield {
155
+ type: "error",
156
+ message: detail || `Claude exited with code ${code ?? "unknown"}${signal ? ` (${signal})` : ""}`,
157
+ };
158
+ }
125
159
  }
126
- completed = true;
127
160
  } finally {
128
- if (!completed && proc.exitCode === null && proc.signalCode === null) {
129
- proc.kill("SIGTERM");
130
- }
131
- const { code, signal } = await exitPromise;
132
- if ((code ?? 0) !== 0 && !sawError) {
133
- const detail = stderr.join("").trim();
134
- yield {
135
- type: "error",
136
- message: detail || `Claude exited with code ${code ?? "unknown"}${signal ? ` (${signal})` : ""}`,
137
- };
138
- }
161
+ releasePreparedAttachments();
139
162
  }
140
163
  }
141
164
 
package/src/index.ts CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import * as os from "node:os";
4
4
  import { ParallAgentGateway, parseShutdownDeadlineMs } from "@parall/agent-core";
5
- import { ParallClient, ParallWs } from "@parall/sdk";
5
+ import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
6
6
  import {
7
7
  buildClaudeRuntimeKey,
8
8
  resolveClaudeAgentConfig,
@@ -22,6 +22,18 @@ function createLogger(prefix: string) {
22
22
  };
23
23
  }
24
24
 
25
+ async function getAgentMeWithLegacyFallback(client: ParallClient, orgId: string) {
26
+ try {
27
+ return await client.getAgentMe(orgId);
28
+ } catch (err) {
29
+ if (!(err instanceof ApiError && err.status === 404)) {
30
+ throw err;
31
+ }
32
+ const user = await client.getMe();
33
+ return { ...user, agent_profile: null };
34
+ }
35
+ }
36
+
25
37
  async function main() {
26
38
  const config = resolveClaudeAgentConfig(process.env);
27
39
  const log = createLogger("claude-agent");
@@ -44,9 +56,13 @@ async function main() {
44
56
  swimlaneName: config.swimlaneName,
45
57
  });
46
58
 
47
- const me = await client.getMe();
59
+ const me = await getAgentMeWithLegacyFallback(client, config.orgId);
48
60
  const agentUserId = me.id;
49
- ensureClaudeWorkspace(config.workspaceDir, log, { userId: agentUserId, displayName: me.display_name });
61
+ ensureClaudeWorkspace(config.workspaceDir, log, {
62
+ userId: agentUserId,
63
+ displayName: me.display_name,
64
+ description: me.agent_profile?.description ?? undefined,
65
+ });
50
66
  const runtimeKey = config.runtimeKey || buildClaudeRuntimeKey(agentUserId);
51
67
  const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
52
68
  const sessionManager = new ClaudeSessionManager(
package/src/workspace.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  buildIdentity,
8
8
  } from "@parall/agent-core";
9
9
  import type { AgentIdentity } from "@parall/agent-core";
10
+ import { ensureLocalAttachmentGitExclude } from "@parall/agent-core/internal/attachment-input";
10
11
 
11
12
  export function ensureClaudeWorkspace(
12
13
  workspaceDir: string,
@@ -43,4 +44,5 @@ export function ensureClaudeWorkspace(
43
44
  }
44
45
  }
45
46
  fs.writeFileSync(claudeMdPath, CLAUDE_MD, "utf8");
47
+ ensureLocalAttachmentGitExclude(workspaceDir);
46
48
  }