@parall/claude-agent 1.57.2 → 1.58.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.
@@ -1,11 +1,15 @@
1
1
  import type { CleanupForkOpts, DispatchAdapter, DispatchInputLifecycle, DispatchOpts, ForkOpts, RuntimeEvent } from '@parall/agent-core';
2
2
  import type { ClaudeAgentConfig } from './config.js';
3
3
  import type { ClaudeSessionManager } from './session-manager.js';
4
+ import { buildSpawnEnv } from './spawn-env.js';
5
+ export { buildSpawnEnv };
4
6
  type ClaudeCodeAdapterOptions = Pick<ClaudeAgentConfig, 'additionalDirs' | 'allowApiKey' | 'allowedTools' | 'appendSystemPrompt' | 'claudeBin' | 'claudeHome' | 'disallowedTools' | 'model' | 'permissionMode' | 'workspaceDir'> & {
5
7
  sessionManager: ClaudeSessionManager;
6
8
  apiUrl: string;
7
9
  apiKey: string;
8
10
  orgId: string;
11
+ /** Catalog context size for the selected model. */
12
+ contextWindow?: number;
9
13
  contextFilePathForSession?: (sessionKey: string) => string;
10
14
  /** @deprecated Use contextFilePathForSession. */
11
15
  stepIdFilePathForSession?: (sessionKey: string) => string;
@@ -27,12 +31,6 @@ type ClaudeCodeAdapterOptions = Pick<ClaudeAgentConfig, 'additionalDirs' | 'allo
27
31
  */
28
32
  capabilityBinDir?: string;
29
33
  };
30
- export declare function buildSpawnEnv(parentEnv: NodeJS.ProcessEnv, claudeHome: string, context: DispatchOpts['context'], opts: {
31
- allowApiKey: boolean;
32
- effortLevel?: string;
33
- wikiMountRoot?: string;
34
- capabilityBinDir?: string;
35
- }): NodeJS.ProcessEnv;
36
34
  export declare class ClaudeCodeAdapter implements DispatchAdapter {
37
35
  private readonly opts;
38
36
  readonly inputLifecycleMode: "explicit";
@@ -42,12 +40,14 @@ export declare class ClaudeCodeAdapter implements DispatchAdapter {
42
40
  private shuttingDown;
43
41
  private _model;
44
42
  private _effortLevel;
43
+ private _contextWindow;
45
44
  constructor(opts: ClaudeCodeAdapterOptions);
46
45
  get currentModel(): string | undefined;
47
46
  get currentEffort(): string | undefined;
48
47
  updateConfig(config: {
49
48
  model?: string | null;
50
49
  effort?: string | null;
50
+ contextWindow?: number | null;
51
51
  }): void;
52
52
  requestProcessRestart(): void;
53
53
  enqueueDuringDispatch(sessionKey: string, body: string, inputLifecycle?: DispatchInputLifecycle): boolean;
@@ -85,5 +85,4 @@ export declare class ClaudeCodeAdapter implements DispatchAdapter {
85
85
  private buildArgs;
86
86
  private buildPlaceholderContext;
87
87
  }
88
- export {};
89
88
  //# sourceMappingURL=dispatch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"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"}
package/dist/dispatch.js CHANGED
@@ -1,11 +1,13 @@
1
+ import { execSync, spawn } from 'node:child_process';
2
+ import { randomUUID } from 'node:crypto';
1
3
  import * as fs from 'node:fs';
2
4
  import * as path from 'node:path';
3
- import { randomUUID } from 'node:crypto';
4
- import { execSync, spawn } from 'node:child_process';
5
5
  import { appendPreparedLocalAttachmentRefs, pinLocalAttachmentPaths, } from '@parall/agent-core/internal/attachment-input';
6
6
  import { ClaudeInputRegistry } from './input-lifecycle.js';
7
7
  import { parseClaudeStreamJson, } from './output-parser.js';
8
+ import { buildSpawnEnv } from './spawn-env.js';
8
9
  import { classifyClaudeTurn } from './turn-outcome.js';
10
+ export { buildSpawnEnv };
9
11
  const IS_WIN32 = process.platform === 'win32';
10
12
  const CAPABILITY_PROBE_TIMEOUT_MS = 15_000;
11
13
  class MissingClaudeLifecycleCapabilityError extends Error {
@@ -24,58 +26,6 @@ function killWin32Tree(pid) {
24
26
  return false;
25
27
  }
26
28
  }
27
- export function buildSpawnEnv(parentEnv, claudeHome, context, opts) {
28
- const env = { ...parentEnv };
29
- if (!opts.allowApiKey) {
30
- delete env.ANTHROPIC_API_KEY;
31
- delete env.ANTHROPIC_AUTH_TOKEN;
32
- }
33
- const result = {
34
- ...env,
35
- HOME: claudeHome,
36
- PRLL_API_URL: context.apiUrl,
37
- PRLL_API_KEY: context.apiKey,
38
- PRLL_ORG_ID: context.orgId,
39
- PRLL_SESSION_ID: context.sessionId ?? '',
40
- PRLL_CHAT_ID: context.chatId ?? '',
41
- PRLL_TRIGGER_MESSAGE_ID: context.triggerMessageId ?? '',
42
- PRLL_NO_REPLY: context.noReply ? '1' : '',
43
- PRLL_CONTEXT_FILE: context.contextFilePath ?? '',
44
- PRLL_STEP_ID_FILE: context.stepIdFilePath ?? '',
45
- // Per-lane dispatch context directory (stable for the bridge's lifetime,
46
- // so pinning it at spawn is safe even for long-lived subprocesses). The
47
- // CLI keys into it by send target to derive dispatch effect keys.
48
- PRLL_CONTEXT_DIR: context.contextDirPath ?? '',
49
- };
50
- // Pin the wiki workspace to the agent workspace (same convention as the
51
- // OpenClaw runtime). Without this, `parall wiki` resolves mounts relative
52
- // to the subprocess cwd — a `cd` into any subdirectory would silently
53
- // split the agent across two wiki workspaces. Operator-set env wins.
54
- if (!result.PRLL_WIKI_MOUNT_ROOT?.trim() && opts.wikiMountRoot) {
55
- result.PRLL_WIKI_MOUNT_ROOT = opts.wikiMountRoot;
56
- }
57
- if (opts.effortLevel) {
58
- result.CLAUDE_CODE_EFFORT_LEVEL = opts.effortLevel;
59
- }
60
- // Channel-capability shims resolve ahead of any globally-installed CLI of
61
- // the same name (the shim owns auth; the real binary is found further down
62
- // the PATH by the shim itself). On Windows env keys are case-insensitive and
63
- // the host key is usually `Path`; after the plain-object spread a literal
64
- // `PATH` write would SPLIT the key (undefined which wins in the child), so
65
- // reuse the parent's casing. On POSIX keys are case-SENSITIVE — `PATH` is
66
- // the real one and a stray `Path` is unrelated, so match exactly there or we
67
- // could prepend to the wrong variable and silently bypass the shim.
68
- if (opts.capabilityBinDir) {
69
- const pathKey = process.platform === 'win32'
70
- ? (Object.keys(result).find((k) => k.toUpperCase() === 'PATH') ?? 'PATH')
71
- : 'PATH';
72
- const existing = result[pathKey];
73
- result[pathKey] = existing
74
- ? `${opts.capabilityBinDir}${path.delimiter}${existing}`
75
- : opts.capabilityBinDir;
76
- }
77
- return result;
78
- }
79
29
  export class ClaudeCodeAdapter {
80
30
  opts;
81
31
  inputLifecycleMode = 'explicit';
@@ -85,9 +35,11 @@ export class ClaudeCodeAdapter {
85
35
  shuttingDown = false;
86
36
  _model;
87
37
  _effortLevel;
38
+ _contextWindow;
88
39
  constructor(opts) {
89
40
  this.opts = opts;
90
41
  this._model = opts.model;
42
+ this._contextWindow = opts.contextWindow;
91
43
  }
92
44
  get currentModel() {
93
45
  return this._model;
@@ -98,11 +50,14 @@ export class ClaudeCodeAdapter {
98
50
  updateConfig(config) {
99
51
  const modelChanged = config.model !== undefined && config.model !== this._model;
100
52
  const effortChanged = config.effort !== undefined && config.effort !== this._effortLevel;
53
+ const contextWindowChanged = config.contextWindow !== undefined && config.contextWindow !== this._contextWindow;
101
54
  if (modelChanged)
102
55
  this._model = config.model ?? undefined;
103
56
  if (effortChanged)
104
57
  this._effortLevel = config.effort ?? undefined;
105
- if (modelChanged || effortChanged) {
58
+ if (contextWindowChanged)
59
+ this._contextWindow = config.contextWindow ?? undefined;
60
+ if (modelChanged || effortChanged || contextWindowChanged) {
106
61
  this.requestProcessRestart();
107
62
  }
108
63
  }
@@ -557,11 +512,12 @@ export class ClaudeCodeAdapter {
557
512
  const args = this.buildArgs(sessionKey, resume);
558
513
  const env = buildSpawnEnv(process.env, this.opts.claudeHome, this.buildPlaceholderContext(sessionKey), {
559
514
  allowApiKey: this.opts.allowApiKey,
515
+ contextWindow: this._contextWindow,
560
516
  effortLevel: this._effortLevel,
561
517
  wikiMountRoot: this.opts.workspaceDir,
562
518
  capabilityBinDir: this.opts.capabilityBinDir,
563
519
  });
564
- log?.info(`spawn long-lived ${this.opts.claudeBin} (session ${sessionKey}, model=${this._model || 'default'}, effort=${this._effortLevel || 'default'}, mode=${this.opts.permissionMode})`);
520
+ log?.info(`spawn long-lived ${this.opts.claudeBin} (session ${sessionKey}, model=${this._model || 'default'}, context=${this._contextWindow || 'cli-default'}, effort=${this._effortLevel || 'default'}, mode=${this.opts.permissionMode})`);
565
521
  const proc = spawn(IS_WIN32 ? quoteWin32Arg(this.opts.claudeBin) : this.opts.claudeBin, IS_WIN32 ? args.map(quoteWin32Arg) : args, {
566
522
  cwd: this.opts.workspaceDir,
567
523
  env,
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import * as os from 'node:os';
3
- import { ParallAgentGateway, capabilityBinDir, configureHttpKeepAlive, createPlatformConfigManager, createLogger, createOtelLogger, childLogger, deriveModelIsPin, materializeChannelCapabilities, resolveRuntimeModel, parseShutdownDeadlineMs, parseForkDeadlineMs, parseDispatchDeadlineMs, parseProviderConfig, clearAllProviderCreds, llmSource, identityFromMe, initAgentTelemetry, } from '@parall/agent-core';
3
+ import { capabilityBinDir, childLogger, clearAllProviderCreds, configureHttpKeepAlive, createLogger, createOtelLogger, createPlatformConfigManager, deriveModelIsPin, identityFromMe, initAgentTelemetry, llmSource, materializeChannelCapabilities, ParallAgentGateway, parseDispatchDeadlineMs, parseForkDeadlineMs, parseProviderConfig, parseShutdownDeadlineMs, resolveRuntimeContextWindow, resolveRuntimeModel, } from '@parall/agent-core';
4
4
  import { ApiError, ParallClient, ParallWs } from '@parall/sdk';
5
5
  import { buildClaudeRuntimeKey, contextFilePathForSession, dispatchContextDirPath, resolveClaudeAgentConfig, resolveWsUrl, sessionStateFilePathForRuntime, stepIdFilePathForSession, } from './config.js';
6
6
  import { ClaudeCodeAdapter } from './dispatch.js';
@@ -22,10 +22,10 @@ async function getAgentMeWithLegacyFallback(client, orgId) {
22
22
  }
23
23
  function resolveProviderEnv() {
24
24
  const pc = parseProviderConfig(process.env);
25
+ const source = llmSource(pc);
25
26
  if (!pc)
26
- return;
27
+ return source;
27
28
  clearAllProviderCreds(process.env);
28
- const source = llmSource(pc);
29
29
  if (source === 'parall') {
30
30
  process.env.ANTHROPIC_AUTH_TOKEN = process.env.PRLL_API_KEY;
31
31
  process.env.ANTHROPIC_BASE_URL = `${process.env.PRLL_API_URL}/api/llm`;
@@ -39,6 +39,7 @@ function resolveProviderEnv() {
39
39
  if (pc.anthropic_base_url)
40
40
  process.env.ANTHROPIC_BASE_URL = pc.anthropic_base_url;
41
41
  }
42
+ return source;
42
43
  }
43
44
  async function main() {
44
45
  // Before any fetch: long-lived HTTP connections for every bridge→api call.
@@ -46,7 +47,7 @@ async function main() {
46
47
  const telemetry = await initAgentTelemetry('parall-claude-agent', 'claude-code');
47
48
  activeLog = createOtelLogger('agent', 'claude-agent');
48
49
  try {
49
- resolveProviderEnv();
50
+ const activeLLMSource = resolveProviderEnv();
50
51
  const config = resolveClaudeAgentConfig(process.env);
51
52
  if (config.allowApiKey && (process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN)) {
52
53
  // Surface the billing path: default is OAuth (Claude.ai subscription); this
@@ -111,17 +112,21 @@ async function main() {
111
112
  // server only when pinned, so we no longer gate it on a local flag.
112
113
  const platformModelOverride = deriveModelIsPin(platformDefaults, me.agent_profile);
113
114
  const resolvedModel = resolveRuntimeModel(platformModelOverride, platformDefaults.model, config.model);
115
+ const resolvedContextWindow = resolveRuntimeContextWindow(resolvedModel, platformDefaults, activeLLMSource);
114
116
  const resolvedEffort = platformDefaults.thinkingEffort ??
115
117
  (process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || undefined);
116
118
  if (platformDefaults.model)
117
119
  agentLog.info(`platform config: model=${platformDefaults.model} → resolved=${resolvedModel ?? 'cli-default'} (${platformModelOverride ? 'pin/override' : 'floor'})`);
118
120
  if (platformDefaults.thinkingEffort)
119
121
  agentLog.info(`platform config: thinking_effort=${platformDefaults.thinkingEffort}`);
122
+ if (resolvedContextWindow)
123
+ agentLog.info(`platform config: context_window=${resolvedContextWindow}`);
120
124
  const adapter = new ClaudeCodeAdapter({
121
125
  claudeBin: config.claudeBin,
122
126
  claudeHome: config.claudeHome,
123
127
  workspaceDir: config.workspaceDir,
124
128
  model: resolvedModel,
129
+ contextWindow: resolvedContextWindow,
125
130
  permissionMode: config.permissionMode,
126
131
  allowedTools: config.allowedTools,
127
132
  disallowedTools: config.disallowedTools,
@@ -170,8 +175,10 @@ async function main() {
170
175
  }
171
176
  const isPin = deriveModelIsPin(updated, lastKnownAgentProfile);
172
177
  const localEffort = process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || null;
178
+ const model = resolveRuntimeModel(isPin, updated.model, config.model) ?? null;
173
179
  adapter.updateConfig({
174
- model: resolveRuntimeModel(isPin, updated.model, config.model) ?? null,
180
+ model,
181
+ contextWindow: resolveRuntimeContextWindow(model, updated, activeLLMSource) ?? null,
175
182
  effort: updated.thinkingEffort ?? localEffort,
176
183
  });
177
184
  // Channel capabilities heat-update with the config: re-materialize the
@@ -0,0 +1,9 @@
1
+ import type { DispatchOpts } from '@parall/agent-core';
2
+ export declare function buildSpawnEnv(parentEnv: NodeJS.ProcessEnv, claudeHome: string, context: DispatchOpts['context'], opts: {
3
+ allowApiKey: boolean;
4
+ contextWindow?: number;
5
+ effortLevel?: string;
6
+ wikiMountRoot?: string;
7
+ capabilityBinDir?: string;
8
+ }): NodeJS.ProcessEnv;
9
+ //# sourceMappingURL=spawn-env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn-env.d.ts","sourceRoot":"","sources":["../src/spawn-env.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,CAAC,UAAU,EAC5B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,EAChC,IAAI,EAAE;IACJ,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GACA,MAAM,CAAC,UAAU,CA8DnB"}
@@ -0,0 +1,61 @@
1
+ import * as path from 'node:path';
2
+ export function buildSpawnEnv(parentEnv, claudeHome, context, opts) {
3
+ const env = { ...parentEnv };
4
+ if (!opts.allowApiKey) {
5
+ delete env.ANTHROPIC_API_KEY;
6
+ delete env.ANTHROPIC_AUTH_TOKEN;
7
+ }
8
+ const result = {
9
+ ...env,
10
+ HOME: claudeHome,
11
+ PRLL_API_URL: context.apiUrl,
12
+ PRLL_API_KEY: context.apiKey,
13
+ PRLL_ORG_ID: context.orgId,
14
+ PRLL_SESSION_ID: context.sessionId ?? '',
15
+ PRLL_CHAT_ID: context.chatId ?? '',
16
+ PRLL_TRIGGER_MESSAGE_ID: context.triggerMessageId ?? '',
17
+ PRLL_NO_REPLY: context.noReply ? '1' : '',
18
+ PRLL_CONTEXT_FILE: context.contextFilePath ?? '',
19
+ PRLL_STEP_ID_FILE: context.stepIdFilePath ?? '',
20
+ // Per-lane dispatch context directory (stable for the bridge's lifetime,
21
+ // so pinning it at spawn is safe even for long-lived subprocesses). The
22
+ // CLI keys into it by send target to derive dispatch effect keys.
23
+ PRLL_CONTEXT_DIR: context.contextDirPath ?? '',
24
+ };
25
+ // Pin the wiki workspace to the agent workspace (same convention as the
26
+ // OpenClaw runtime). Without this, `parall wiki` resolves mounts relative
27
+ // to the subprocess cwd — a `cd` into any subdirectory would silently
28
+ // split the agent across two wiki workspaces. Operator-set env wins.
29
+ if (!result.PRLL_WIKI_MOUNT_ROOT?.trim() && opts.wikiMountRoot) {
30
+ result.PRLL_WIKI_MOUNT_ROOT = opts.wikiMountRoot;
31
+ }
32
+ if (opts.effortLevel) {
33
+ result.CLAUDE_CODE_EFFORT_LEVEL = opts.effortLevel;
34
+ }
35
+ if (typeof opts.contextWindow === 'number' &&
36
+ Number.isSafeInteger(opts.contextWindow) &&
37
+ opts.contextWindow > 0) {
38
+ // Claude Code otherwise assumes 200k for an unrecognized model ID. Its
39
+ // own auto-compaction policy reserves the output buffer before this
40
+ // declared boundary; Parall supplies the model fact, not a second ratio.
41
+ result.CLAUDE_CODE_MAX_CONTEXT_TOKENS = String(opts.contextWindow);
42
+ }
43
+ // Channel-capability shims resolve ahead of any globally-installed CLI of
44
+ // the same name (the shim owns auth; the real binary is found further down
45
+ // the PATH by the shim itself). On Windows env keys are case-insensitive and
46
+ // the host key is usually `Path`; after the plain-object spread a literal
47
+ // `PATH` write would SPLIT the key (undefined which wins in the child), so
48
+ // reuse the parent's casing. On POSIX keys are case-SENSITIVE — `PATH` is
49
+ // the real one and a stray `Path` is unrelated, so match exactly there or we
50
+ // could prepend to the wrong variable and silently bypass the shim.
51
+ if (opts.capabilityBinDir) {
52
+ const pathKey = process.platform === 'win32'
53
+ ? (Object.keys(result).find((k) => k.toUpperCase() === 'PATH') ?? 'PATH')
54
+ : 'PATH';
55
+ const existing = result[pathKey];
56
+ result[pathKey] = existing
57
+ ? `${opts.capabilityBinDir}${path.delimiter}${existing}`
58
+ : opts.capabilityBinDir;
59
+ }
60
+ return result;
61
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/claude-agent",
3
- "version": "1.57.2",
3
+ "version": "1.58.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/agent-core": "1.57.2",
29
- "@parall/sdk": "1.57.2",
30
- "@parall/cli": "1.57.2"
28
+ "@parall/agent-core": "1.58.0",
29
+ "@parall/cli": "1.58.0",
30
+ "@parall/sdk": "1.58.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^22.0.0",
package/src/dispatch.ts CHANGED
@@ -1,11 +1,7 @@
1
+ import { execSync, spawn } from 'node:child_process';
2
+ import { randomUUID } from 'node:crypto';
1
3
  import * as fs from 'node:fs';
2
4
  import * as path from 'node:path';
3
- import { randomUUID } from 'node:crypto';
4
- import { execSync, spawn } from 'node:child_process';
5
- import {
6
- appendPreparedLocalAttachmentRefs,
7
- pinLocalAttachmentPaths,
8
- } from '@parall/agent-core/internal/attachment-input';
9
5
  import type {
10
6
  CleanupForkOpts,
11
7
  DispatchAdapter,
@@ -15,16 +11,23 @@ import type {
15
11
  GatewayLogger,
16
12
  RuntimeEvent,
17
13
  } from '@parall/agent-core';
14
+ import {
15
+ appendPreparedLocalAttachmentRefs,
16
+ pinLocalAttachmentPaths,
17
+ } from '@parall/agent-core/internal/attachment-input';
18
18
  import type { ClaudeAgentConfig } from './config.js';
19
- import { ClaudeInputRegistry, type ClaudeInputDelivery } from './input-lifecycle.js';
19
+ import { type ClaudeInputDelivery, ClaudeInputRegistry } from './input-lifecycle.js';
20
20
  import {
21
- parseClaudeStreamJson,
22
21
  type ClaudeParsedEvent,
23
22
  type ClaudeResultMeta,
23
+ parseClaudeStreamJson,
24
24
  } from './output-parser.js';
25
25
  import type { ClaudeProcessHandle, ClaudeSessionManager } from './session-manager.js';
26
+ import { buildSpawnEnv } from './spawn-env.js';
26
27
  import { classifyClaudeTurn } from './turn-outcome.js';
27
28
 
29
+ export { buildSpawnEnv };
30
+
28
31
  type ClaudeCodeAdapterOptions = Pick<
29
32
  ClaudeAgentConfig,
30
33
  | 'additionalDirs'
@@ -42,6 +45,8 @@ type ClaudeCodeAdapterOptions = Pick<
42
45
  apiUrl: string;
43
46
  apiKey: string;
44
47
  orgId: string;
48
+ /** Catalog context size for the selected model. */
49
+ contextWindow?: number;
45
50
  contextFilePathForSession?: (sessionKey: string) => string;
46
51
  /** @deprecated Use contextFilePathForSession. */
47
52
  stepIdFilePathForSession?: (sessionKey: string) => string;
@@ -83,70 +88,6 @@ function killWin32Tree(pid: number): boolean {
83
88
  }
84
89
  }
85
90
 
86
- export function buildSpawnEnv(
87
- parentEnv: NodeJS.ProcessEnv,
88
- claudeHome: string,
89
- context: DispatchOpts['context'],
90
- opts: {
91
- allowApiKey: boolean;
92
- effortLevel?: string;
93
- wikiMountRoot?: string;
94
- capabilityBinDir?: string;
95
- },
96
- ): NodeJS.ProcessEnv {
97
- const env: NodeJS.ProcessEnv = { ...parentEnv };
98
- if (!opts.allowApiKey) {
99
- delete env.ANTHROPIC_API_KEY;
100
- delete env.ANTHROPIC_AUTH_TOKEN;
101
- }
102
- const result: NodeJS.ProcessEnv = {
103
- ...env,
104
- HOME: claudeHome,
105
- PRLL_API_URL: context.apiUrl,
106
- PRLL_API_KEY: context.apiKey,
107
- PRLL_ORG_ID: context.orgId,
108
- PRLL_SESSION_ID: context.sessionId ?? '',
109
- PRLL_CHAT_ID: context.chatId ?? '',
110
- PRLL_TRIGGER_MESSAGE_ID: context.triggerMessageId ?? '',
111
- PRLL_NO_REPLY: context.noReply ? '1' : '',
112
- PRLL_CONTEXT_FILE: context.contextFilePath ?? '',
113
- PRLL_STEP_ID_FILE: context.stepIdFilePath ?? '',
114
- // Per-lane dispatch context directory (stable for the bridge's lifetime,
115
- // so pinning it at spawn is safe even for long-lived subprocesses). The
116
- // CLI keys into it by send target to derive dispatch effect keys.
117
- PRLL_CONTEXT_DIR: context.contextDirPath ?? '',
118
- };
119
- // Pin the wiki workspace to the agent workspace (same convention as the
120
- // OpenClaw runtime). Without this, `parall wiki` resolves mounts relative
121
- // to the subprocess cwd — a `cd` into any subdirectory would silently
122
- // split the agent across two wiki workspaces. Operator-set env wins.
123
- if (!result.PRLL_WIKI_MOUNT_ROOT?.trim() && opts.wikiMountRoot) {
124
- result.PRLL_WIKI_MOUNT_ROOT = opts.wikiMountRoot;
125
- }
126
- if (opts.effortLevel) {
127
- result.CLAUDE_CODE_EFFORT_LEVEL = opts.effortLevel;
128
- }
129
- // Channel-capability shims resolve ahead of any globally-installed CLI of
130
- // the same name (the shim owns auth; the real binary is found further down
131
- // the PATH by the shim itself). On Windows env keys are case-insensitive and
132
- // the host key is usually `Path`; after the plain-object spread a literal
133
- // `PATH` write would SPLIT the key (undefined which wins in the child), so
134
- // reuse the parent's casing. On POSIX keys are case-SENSITIVE — `PATH` is
135
- // the real one and a stray `Path` is unrelated, so match exactly there or we
136
- // could prepend to the wrong variable and silently bypass the shim.
137
- if (opts.capabilityBinDir) {
138
- const pathKey =
139
- process.platform === 'win32'
140
- ? (Object.keys(result).find((k) => k.toUpperCase() === 'PATH') ?? 'PATH')
141
- : 'PATH';
142
- const existing = result[pathKey];
143
- result[pathKey] = existing
144
- ? `${opts.capabilityBinDir}${path.delimiter}${existing}`
145
- : opts.capabilityBinDir;
146
- }
147
- return result;
148
- }
149
-
150
91
  /**
151
92
  * Per-sessionKey long-lived process state. The process stays alive across
152
93
  * dispatches; each dispatch writes one NDJSON user message to stdin and
@@ -171,9 +112,11 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
171
112
  private shuttingDown = false;
172
113
  private _model: string | undefined;
173
114
  private _effortLevel: string | undefined;
115
+ private _contextWindow: number | undefined;
174
116
 
175
117
  constructor(private readonly opts: ClaudeCodeAdapterOptions) {
176
118
  this._model = opts.model;
119
+ this._contextWindow = opts.contextWindow;
177
120
  }
178
121
 
179
122
  get currentModel(): string | undefined {
@@ -182,13 +125,19 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
182
125
  get currentEffort(): string | undefined {
183
126
  return this._effortLevel;
184
127
  }
185
-
186
- updateConfig(config: { model?: string | null; effort?: string | null }): void {
128
+ updateConfig(config: {
129
+ model?: string | null;
130
+ effort?: string | null;
131
+ contextWindow?: number | null;
132
+ }): void {
187
133
  const modelChanged = config.model !== undefined && config.model !== this._model;
188
134
  const effortChanged = config.effort !== undefined && config.effort !== this._effortLevel;
135
+ const contextWindowChanged =
136
+ config.contextWindow !== undefined && config.contextWindow !== this._contextWindow;
189
137
  if (modelChanged) this._model = config.model ?? undefined;
190
138
  if (effortChanged) this._effortLevel = config.effort ?? undefined;
191
- if (modelChanged || effortChanged) {
139
+ if (contextWindowChanged) this._contextWindow = config.contextWindow ?? undefined;
140
+ if (modelChanged || effortChanged || contextWindowChanged) {
192
141
  this.requestProcessRestart();
193
142
  }
194
143
  }
@@ -714,6 +663,7 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
714
663
  this.buildPlaceholderContext(sessionKey),
715
664
  {
716
665
  allowApiKey: this.opts.allowApiKey,
666
+ contextWindow: this._contextWindow,
717
667
  effortLevel: this._effortLevel,
718
668
  wikiMountRoot: this.opts.workspaceDir,
719
669
  capabilityBinDir: this.opts.capabilityBinDir,
@@ -721,7 +671,7 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
721
671
  );
722
672
 
723
673
  log?.info(
724
- `spawn long-lived ${this.opts.claudeBin} (session ${sessionKey}, model=${this._model || 'default'}, effort=${this._effortLevel || 'default'}, mode=${this.opts.permissionMode})`,
674
+ `spawn long-lived ${this.opts.claudeBin} (session ${sessionKey}, model=${this._model || 'default'}, context=${this._contextWindow || 'cli-default'}, effort=${this._effortLevel || 'default'}, mode=${this.opts.permissionMode})`,
725
675
  );
726
676
 
727
677
  const proc = spawn(
package/src/index.ts CHANGED
@@ -2,24 +2,25 @@
2
2
 
3
3
  import * as os from 'node:os';
4
4
  import {
5
- ParallAgentGateway,
6
5
  capabilityBinDir,
6
+ childLogger,
7
+ clearAllProviderCreds,
7
8
  configureHttpKeepAlive,
8
- createPlatformConfigManager,
9
9
  createLogger,
10
10
  createOtelLogger,
11
- childLogger,
11
+ createPlatformConfigManager,
12
12
  deriveModelIsPin,
13
+ identityFromMe,
14
+ initAgentTelemetry,
15
+ llmSource,
13
16
  materializeChannelCapabilities,
14
- resolveRuntimeModel,
15
- parseShutdownDeadlineMs,
16
- parseForkDeadlineMs,
17
+ ParallAgentGateway,
17
18
  parseDispatchDeadlineMs,
19
+ parseForkDeadlineMs,
18
20
  parseProviderConfig,
19
- clearAllProviderCreds,
20
- llmSource,
21
- identityFromMe,
22
- initAgentTelemetry,
21
+ parseShutdownDeadlineMs,
22
+ resolveRuntimeContextWindow,
23
+ resolveRuntimeModel,
23
24
  } from '@parall/agent-core';
24
25
  import { ApiError, ParallClient, ParallWs } from '@parall/sdk';
25
26
  import {
@@ -52,11 +53,11 @@ async function getAgentMeWithLegacyFallback(client: ParallClient, orgId: string)
52
53
 
53
54
  type AgentMe = Awaited<ReturnType<typeof getAgentMeWithLegacyFallback>>;
54
55
 
55
- function resolveProviderEnv(): void {
56
+ function resolveProviderEnv(): string {
56
57
  const pc = parseProviderConfig(process.env);
57
- if (!pc) return;
58
- clearAllProviderCreds(process.env);
59
58
  const source = llmSource(pc);
59
+ if (!pc) return source;
60
+ clearAllProviderCreds(process.env);
60
61
  if (source === 'parall') {
61
62
  process.env.ANTHROPIC_AUTH_TOKEN = process.env.PRLL_API_KEY;
62
63
  process.env.ANTHROPIC_BASE_URL = `${process.env.PRLL_API_URL}/api/llm`;
@@ -68,6 +69,7 @@ function resolveProviderEnv(): void {
68
69
  }
69
70
  if (pc.anthropic_base_url) process.env.ANTHROPIC_BASE_URL = pc.anthropic_base_url;
70
71
  }
72
+ return source;
71
73
  }
72
74
 
73
75
  async function main() {
@@ -76,7 +78,7 @@ async function main() {
76
78
  const telemetry = await initAgentTelemetry('parall-claude-agent', 'claude-code');
77
79
  activeLog = createOtelLogger('agent', 'claude-agent');
78
80
  try {
79
- resolveProviderEnv();
81
+ const activeLLMSource = resolveProviderEnv();
80
82
  const config = resolveClaudeAgentConfig(process.env);
81
83
 
82
84
  if (config.allowApiKey && (process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN)) {
@@ -152,6 +154,11 @@ async function main() {
152
154
  platformDefaults.model,
153
155
  config.model,
154
156
  );
157
+ const resolvedContextWindow = resolveRuntimeContextWindow(
158
+ resolvedModel,
159
+ platformDefaults,
160
+ activeLLMSource,
161
+ );
155
162
  const resolvedEffort =
156
163
  platformDefaults.thinkingEffort ??
157
164
  (process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || undefined);
@@ -161,12 +168,15 @@ async function main() {
161
168
  );
162
169
  if (platformDefaults.thinkingEffort)
163
170
  agentLog.info(`platform config: thinking_effort=${platformDefaults.thinkingEffort}`);
171
+ if (resolvedContextWindow)
172
+ agentLog.info(`platform config: context_window=${resolvedContextWindow}`);
164
173
 
165
174
  const adapter = new ClaudeCodeAdapter({
166
175
  claudeBin: config.claudeBin,
167
176
  claudeHome: config.claudeHome,
168
177
  workspaceDir: config.workspaceDir,
169
178
  model: resolvedModel,
179
+ contextWindow: resolvedContextWindow,
170
180
  permissionMode: config.permissionMode,
171
181
  allowedTools: config.allowedTools,
172
182
  disallowedTools: config.disallowedTools,
@@ -219,8 +229,10 @@ async function main() {
219
229
  }
220
230
  const isPin = deriveModelIsPin(updated, lastKnownAgentProfile);
221
231
  const localEffort = process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || null;
232
+ const model = resolveRuntimeModel(isPin, updated.model, config.model) ?? null;
222
233
  adapter.updateConfig({
223
- model: resolveRuntimeModel(isPin, updated.model, config.model) ?? null,
234
+ model,
235
+ contextWindow: resolveRuntimeContextWindow(model, updated, activeLLMSource) ?? null,
224
236
  effort: updated.thinkingEffort ?? localEffort,
225
237
  });
226
238
  // Channel capabilities heat-update with the config: re-materialize the
@@ -0,0 +1,77 @@
1
+ import * as path from 'node:path';
2
+ import type { DispatchOpts } from '@parall/agent-core';
3
+
4
+ export function buildSpawnEnv(
5
+ parentEnv: NodeJS.ProcessEnv,
6
+ claudeHome: string,
7
+ context: DispatchOpts['context'],
8
+ opts: {
9
+ allowApiKey: boolean;
10
+ contextWindow?: number;
11
+ effortLevel?: string;
12
+ wikiMountRoot?: string;
13
+ capabilityBinDir?: string;
14
+ },
15
+ ): NodeJS.ProcessEnv {
16
+ const env: NodeJS.ProcessEnv = { ...parentEnv };
17
+ if (!opts.allowApiKey) {
18
+ delete env.ANTHROPIC_API_KEY;
19
+ delete env.ANTHROPIC_AUTH_TOKEN;
20
+ }
21
+ const result: NodeJS.ProcessEnv = {
22
+ ...env,
23
+ HOME: claudeHome,
24
+ PRLL_API_URL: context.apiUrl,
25
+ PRLL_API_KEY: context.apiKey,
26
+ PRLL_ORG_ID: context.orgId,
27
+ PRLL_SESSION_ID: context.sessionId ?? '',
28
+ PRLL_CHAT_ID: context.chatId ?? '',
29
+ PRLL_TRIGGER_MESSAGE_ID: context.triggerMessageId ?? '',
30
+ PRLL_NO_REPLY: context.noReply ? '1' : '',
31
+ PRLL_CONTEXT_FILE: context.contextFilePath ?? '',
32
+ PRLL_STEP_ID_FILE: context.stepIdFilePath ?? '',
33
+ // Per-lane dispatch context directory (stable for the bridge's lifetime,
34
+ // so pinning it at spawn is safe even for long-lived subprocesses). The
35
+ // CLI keys into it by send target to derive dispatch effect keys.
36
+ PRLL_CONTEXT_DIR: context.contextDirPath ?? '',
37
+ };
38
+ // Pin the wiki workspace to the agent workspace (same convention as the
39
+ // OpenClaw runtime). Without this, `parall wiki` resolves mounts relative
40
+ // to the subprocess cwd — a `cd` into any subdirectory would silently
41
+ // split the agent across two wiki workspaces. Operator-set env wins.
42
+ if (!result.PRLL_WIKI_MOUNT_ROOT?.trim() && opts.wikiMountRoot) {
43
+ result.PRLL_WIKI_MOUNT_ROOT = opts.wikiMountRoot;
44
+ }
45
+ if (opts.effortLevel) {
46
+ result.CLAUDE_CODE_EFFORT_LEVEL = opts.effortLevel;
47
+ }
48
+ if (
49
+ typeof opts.contextWindow === 'number' &&
50
+ Number.isSafeInteger(opts.contextWindow) &&
51
+ opts.contextWindow > 0
52
+ ) {
53
+ // Claude Code otherwise assumes 200k for an unrecognized model ID. Its
54
+ // own auto-compaction policy reserves the output buffer before this
55
+ // declared boundary; Parall supplies the model fact, not a second ratio.
56
+ result.CLAUDE_CODE_MAX_CONTEXT_TOKENS = String(opts.contextWindow);
57
+ }
58
+ // Channel-capability shims resolve ahead of any globally-installed CLI of
59
+ // the same name (the shim owns auth; the real binary is found further down
60
+ // the PATH by the shim itself). On Windows env keys are case-insensitive and
61
+ // the host key is usually `Path`; after the plain-object spread a literal
62
+ // `PATH` write would SPLIT the key (undefined which wins in the child), so
63
+ // reuse the parent's casing. On POSIX keys are case-SENSITIVE — `PATH` is
64
+ // the real one and a stray `Path` is unrelated, so match exactly there or we
65
+ // could prepend to the wrong variable and silently bypass the shim.
66
+ if (opts.capabilityBinDir) {
67
+ const pathKey =
68
+ process.platform === 'win32'
69
+ ? (Object.keys(result).find((k) => k.toUpperCase() === 'PATH') ?? 'PATH')
70
+ : 'PATH';
71
+ const existing = result[pathKey];
72
+ result[pathKey] = existing
73
+ ? `${opts.capabilityBinDir}${path.delimiter}${existing}`
74
+ : opts.capabilityBinDir;
75
+ }
76
+ return result;
77
+ }