@parall/claude-agent 1.24.0 → 1.26.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/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -0
- package/dist/dispatch.d.ts +24 -0
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +202 -78
- package/dist/index.js +51 -3
- package/dist/output-parser.d.ts +4 -0
- package/dist/output-parser.d.ts.map +1 -1
- package/dist/output-parser.js +19 -5
- package/dist/session-manager.d.ts +23 -0
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/session-manager.js +117 -0
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +7 -22
- package/package.json +4 -4
- package/src/config.ts +6 -0
- package/src/dispatch.ts +259 -85
- package/src/index.ts +50 -2
- package/src/output-parser.ts +31 -6
- package/src/session-manager.ts +144 -0
- package/src/workspace.ts +10 -22
package/dist/config.d.ts
CHANGED
|
@@ -21,5 +21,7 @@ export declare function resolveClaudeAgentConfig(env?: NodeJS.ProcessEnv): Claud
|
|
|
21
21
|
export declare function resolveWsUrl(apiUrl: string, explicitWsUrl?: string, swimlaneName?: string): string;
|
|
22
22
|
export declare function buildClaudeRuntimeKey(agentUserId: string): string;
|
|
23
23
|
export declare function sessionStateFilePathForRuntime(stateDir: string, runtimeKey: string): string;
|
|
24
|
+
export declare function contextFilePathForSession(stateDir: string, sessionKey: string): string;
|
|
25
|
+
/** @deprecated Use contextFilePathForSession. */
|
|
24
26
|
export declare function stepIdFilePathForSession(stateDir: string, sessionKey: string): string;
|
|
25
27
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAsBF,wBAAgB,wBAAwB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,iBAAiB,CA4BhG;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAMlG;AAED,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG3F;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGrF"}
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAsBF,wBAAgB,wBAAwB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,iBAAiB,CA4BhG;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAMlG;AAED,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG3F;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGtF;AAED,iDAAiD;AACjD,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGrF"}
|
package/dist/config.js
CHANGED
|
@@ -61,6 +61,11 @@ export function sessionStateFilePathForRuntime(stateDir, runtimeKey) {
|
|
|
61
61
|
const fileName = Buffer.from(runtimeKey).toString("base64url");
|
|
62
62
|
return path.join(stateDir, "sessions", `${fileName}.json`);
|
|
63
63
|
}
|
|
64
|
+
export function contextFilePathForSession(stateDir, sessionKey) {
|
|
65
|
+
const fileName = Buffer.from(sessionKey).toString("base64url");
|
|
66
|
+
return path.join(stateDir, "dispatch-context", `${fileName}.json`);
|
|
67
|
+
}
|
|
68
|
+
/** @deprecated Use contextFilePathForSession. */
|
|
64
69
|
export function stepIdFilePathForSession(stateDir, sessionKey) {
|
|
65
70
|
const fileName = Buffer.from(sessionKey).toString("base64url");
|
|
66
71
|
return path.join(stateDir, "step-ids", `${fileName}.txt`);
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -3,17 +3,41 @@ import type { ClaudeAgentConfig } from "./config.js";
|
|
|
3
3
|
import { ClaudeSessionManager } from "./session-manager.js";
|
|
4
4
|
type ClaudeCodeAdapterOptions = Pick<ClaudeAgentConfig, "additionalDirs" | "allowApiKey" | "allowedTools" | "appendSystemPrompt" | "claudeBin" | "claudeHome" | "disallowedTools" | "model" | "permissionMode" | "workspaceDir"> & {
|
|
5
5
|
sessionManager: ClaudeSessionManager;
|
|
6
|
+
apiUrl: string;
|
|
7
|
+
apiKey: string;
|
|
8
|
+
orgId: string;
|
|
9
|
+
contextFilePathForSession?: (sessionKey: string) => string;
|
|
10
|
+
/** @deprecated Use contextFilePathForSession. */
|
|
11
|
+
stepIdFilePathForSession?: (sessionKey: string) => string;
|
|
6
12
|
};
|
|
7
13
|
export declare function buildSpawnEnv(parentEnv: NodeJS.ProcessEnv, claudeHome: string, context: DispatchOpts["context"], opts: {
|
|
8
14
|
allowApiKey: boolean;
|
|
15
|
+
effortLevel?: string;
|
|
9
16
|
}): NodeJS.ProcessEnv;
|
|
10
17
|
export declare class ClaudeCodeAdapter implements DispatchAdapter {
|
|
11
18
|
private readonly opts;
|
|
19
|
+
private readonly processes;
|
|
20
|
+
private shuttingDown;
|
|
21
|
+
private _model;
|
|
22
|
+
private _effortLevel;
|
|
12
23
|
constructor(opts: ClaudeCodeAdapterOptions);
|
|
24
|
+
get currentModel(): string | undefined;
|
|
25
|
+
get currentEffort(): string | undefined;
|
|
26
|
+
updateConfig(config: {
|
|
27
|
+
model?: string | null;
|
|
28
|
+
effort?: string | null;
|
|
29
|
+
}): void;
|
|
13
30
|
dispatch({ event, bodyForAgent, sessionKey, context }: DispatchOpts): AsyncIterable<RuntimeEvent>;
|
|
14
31
|
forkSession({ sessionKey }: ForkOpts): import("@parall/agent-core").ForkSessionHandle | null;
|
|
15
32
|
cleanupFork({ fork }: CleanupForkOpts): void;
|
|
33
|
+
shutdown(): Promise<void>;
|
|
34
|
+
private runTurn;
|
|
35
|
+
private ensureProcess;
|
|
36
|
+
private spawnProcess;
|
|
37
|
+
private killProcess;
|
|
38
|
+
private writeUserMessage;
|
|
16
39
|
private buildArgs;
|
|
40
|
+
private buildPlaceholderContext;
|
|
17
41
|
}
|
|
18
42
|
export {};
|
|
19
43
|
//# sourceMappingURL=dispatch.d.ts.map
|
package/dist/dispatch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EAER,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EACL,oBAAoB,EAErB,MAAM,sBAAsB,CAAC;AAE9B,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;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,yBAAyB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3D,iDAAiD;IACjD,wBAAwB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;CAC3D,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,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD,MAAM,CAAC,UAAU,CAuBnB;AAgBD,qBAAa,iBAAkB,YAAW,eAAe;IAM3C,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmC;IAC7D,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,YAAY,CAAqB;gBAEZ,IAAI,EAAE,wBAAwB;IAI3D,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAAwB;IAC9D,IAAI,aAAa,IAAI,MAAM,GAAG,SAAS,CAA8B;IAErE,YAAY,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAYtE,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IAuBxG,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,QAAQ;IAIpC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAQ/B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAShB,OAAO;IAsEtB,OAAO,CAAC,aAAa;IAyBrB,OAAO,CAAC,YAAY;IA0CpB,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,SAAS;IAyCjB,OAAO,CAAC,uBAAuB;CAkBhC"}
|
package/dist/dispatch.js
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { spawn } from "node:child_process";
|
|
3
4
|
import { appendPreparedLocalAttachmentRefs, pinLocalAttachmentPaths, } from "@parall/agent-core/internal/attachment-input";
|
|
4
5
|
import { parseClaudeStreamJson } from "./output-parser.js";
|
|
5
6
|
export function buildSpawnEnv(parentEnv, claudeHome, context, opts) {
|
|
6
7
|
const env = { ...parentEnv };
|
|
7
|
-
// Default: behave like the hosted Claude container — OAuth via
|
|
8
|
-
// ~/.claude/.credentials.json only. Inheriting the operator's
|
|
9
|
-
// ANTHROPIC_API_KEY would silently divert billing to Anthropic API
|
|
10
|
-
// pay-per-use instead of the connected Claude.ai subscription.
|
|
11
|
-
// Opt back in with PRLL_CLAUDE_ALLOW_API_KEY=1.
|
|
12
8
|
if (!opts.allowApiKey) {
|
|
13
9
|
delete env.ANTHROPIC_API_KEY;
|
|
14
10
|
delete env.ANTHROPIC_AUTH_TOKEN;
|
|
15
11
|
}
|
|
16
|
-
|
|
12
|
+
const result = {
|
|
17
13
|
...env,
|
|
18
14
|
HOME: claudeHome,
|
|
19
15
|
PRLL_API_URL: context.apiUrl,
|
|
@@ -23,13 +19,38 @@ export function buildSpawnEnv(parentEnv, claudeHome, context, opts) {
|
|
|
23
19
|
PRLL_CHAT_ID: context.chatId ?? "",
|
|
24
20
|
PRLL_TRIGGER_MESSAGE_ID: context.triggerMessageId ?? "",
|
|
25
21
|
PRLL_NO_REPLY: context.noReply ? "1" : "",
|
|
22
|
+
PRLL_CONTEXT_FILE: context.contextFilePath ?? "",
|
|
26
23
|
PRLL_STEP_ID_FILE: context.stepIdFilePath ?? "",
|
|
27
24
|
};
|
|
25
|
+
if (opts.effortLevel) {
|
|
26
|
+
result.CLAUDE_CODE_EFFORT_LEVEL = opts.effortLevel;
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
28
29
|
}
|
|
29
30
|
export class ClaudeCodeAdapter {
|
|
30
31
|
opts;
|
|
32
|
+
processes = new Map();
|
|
33
|
+
shuttingDown = false;
|
|
34
|
+
_model;
|
|
35
|
+
_effortLevel;
|
|
31
36
|
constructor(opts) {
|
|
32
37
|
this.opts = opts;
|
|
38
|
+
this._model = opts.model;
|
|
39
|
+
}
|
|
40
|
+
get currentModel() { return this._model; }
|
|
41
|
+
get currentEffort() { return this._effortLevel; }
|
|
42
|
+
updateConfig(config) {
|
|
43
|
+
const modelChanged = config.model !== undefined && config.model !== this._model;
|
|
44
|
+
const effortChanged = config.effort !== undefined && config.effort !== this._effortLevel;
|
|
45
|
+
if (modelChanged)
|
|
46
|
+
this._model = config.model ?? undefined;
|
|
47
|
+
if (effortChanged)
|
|
48
|
+
this._effortLevel = config.effort ?? undefined;
|
|
49
|
+
if (modelChanged || effortChanged) {
|
|
50
|
+
for (const [, state] of this.processes) {
|
|
51
|
+
state.needsRestart = true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
33
54
|
}
|
|
34
55
|
async *dispatch({ event, bodyForAgent, sessionKey, context }) {
|
|
35
56
|
let promptBody = bodyForAgent;
|
|
@@ -46,73 +67,7 @@ export class ClaudeCodeAdapter {
|
|
|
46
67
|
context.log?.warn?.(`claude-agent[${context.accountId}]: failed to prepare local attachments: ${String(err)}`);
|
|
47
68
|
}
|
|
48
69
|
try {
|
|
49
|
-
|
|
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
|
-
};
|
|
100
|
-
}
|
|
101
|
-
completed = true;
|
|
102
|
-
}
|
|
103
|
-
finally {
|
|
104
|
-
if (!completed && proc.exitCode === null && proc.signalCode === null) {
|
|
105
|
-
proc.kill("SIGTERM");
|
|
106
|
-
}
|
|
107
|
-
const { code, signal } = await exitPromise;
|
|
108
|
-
if ((code ?? 0) !== 0 && !sawError) {
|
|
109
|
-
const detail = stderr.join("").trim();
|
|
110
|
-
yield {
|
|
111
|
-
type: "error",
|
|
112
|
-
message: detail || `Claude exited with code ${code ?? "unknown"}${signal ? ` (${signal})` : ""}`,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
}
|
|
70
|
+
yield* this.runTurn(sessionKey, promptBody, context.log);
|
|
116
71
|
}
|
|
117
72
|
finally {
|
|
118
73
|
releasePreparedAttachments();
|
|
@@ -122,19 +77,170 @@ export class ClaudeCodeAdapter {
|
|
|
122
77
|
return this.opts.sessionManager.createForkSession(sessionKey);
|
|
123
78
|
}
|
|
124
79
|
cleanupFork({ fork }) {
|
|
80
|
+
const state = this.processes.get(fork.sessionKey);
|
|
81
|
+
if (state) {
|
|
82
|
+
this.killProcess(fork.sessionKey, state);
|
|
83
|
+
}
|
|
125
84
|
this.opts.sessionManager.cleanupFork(fork.sessionKey);
|
|
126
85
|
}
|
|
127
|
-
|
|
86
|
+
async shutdown() {
|
|
87
|
+
this.shuttingDown = true;
|
|
88
|
+
for (const [sessionKey, state] of this.processes) {
|
|
89
|
+
this.killProcess(sessionKey, state);
|
|
90
|
+
}
|
|
91
|
+
this.processes.clear();
|
|
92
|
+
await this.opts.sessionManager.shutdownAll();
|
|
93
|
+
}
|
|
94
|
+
async *runTurn(sessionKey, promptBody, log) {
|
|
95
|
+
let state;
|
|
96
|
+
try {
|
|
97
|
+
state = this.ensureProcess(sessionKey, log);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
yield { type: "error", message: `Claude spawn failed: ${String(err)}` };
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const groupKey = randomUUID();
|
|
104
|
+
let sawError = false;
|
|
105
|
+
try {
|
|
106
|
+
this.writeUserMessage(state.handle, promptBody);
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
yield { type: "error", message: `Claude stdin write failed: ${String(err)}` };
|
|
110
|
+
this.killProcess(sessionKey, state);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
while (true) {
|
|
114
|
+
const next = await state.parser.next();
|
|
115
|
+
if (next.done) {
|
|
116
|
+
state.done = true;
|
|
117
|
+
this.processes.delete(sessionKey);
|
|
118
|
+
if (!sawError) {
|
|
119
|
+
const detail = state.handle.stderrChunks.join("").trim();
|
|
120
|
+
const exit = await state.handle.exitPromise.catch(() => ({ code: null, signal: null }));
|
|
121
|
+
yield {
|
|
122
|
+
type: "error",
|
|
123
|
+
message: detail
|
|
124
|
+
|| `Claude exited with code ${exit.code ?? "unknown"}${exit.signal ? ` (${exit.signal})` : ""}`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const parsed = next.value;
|
|
130
|
+
if (parsed.type === "session_id") {
|
|
131
|
+
this.opts.sessionManager.recordSessionId(sessionKey, parsed.sessionId);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (parsed.type === "turn_end") {
|
|
135
|
+
if (state.needsRestart) {
|
|
136
|
+
this.killProcess(sessionKey, state);
|
|
137
|
+
}
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (parsed.type === "error") {
|
|
141
|
+
sawError = true;
|
|
142
|
+
yield parsed;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (parsed.type === "text") {
|
|
146
|
+
yield { ...parsed, project: false, groupKey };
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
yield { ...parsed, groupKey };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
ensureProcess(sessionKey, log) {
|
|
153
|
+
if (this.shuttingDown) {
|
|
154
|
+
throw new Error("claude-agent: adapter shutting down, refusing new process");
|
|
155
|
+
}
|
|
156
|
+
const existing = this.processes.get(sessionKey);
|
|
157
|
+
if (existing && !existing.done) {
|
|
158
|
+
const { proc } = existing.handle;
|
|
159
|
+
if (existing.needsRestart) {
|
|
160
|
+
this.killProcess(sessionKey, existing);
|
|
161
|
+
}
|
|
162
|
+
else if (proc.exitCode === null && proc.signalCode === null && !proc.stdin.destroyed) {
|
|
163
|
+
return existing;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
this.processes.delete(sessionKey);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const handle = this.spawnProcess(sessionKey, log);
|
|
170
|
+
const parser = parseClaudeStreamJson(handle.proc.stdout);
|
|
171
|
+
const state = { handle, parser, done: false, needsRestart: false };
|
|
172
|
+
this.processes.set(sessionKey, state);
|
|
173
|
+
this.opts.sessionManager.registerProcess(sessionKey, handle);
|
|
174
|
+
return state;
|
|
175
|
+
}
|
|
176
|
+
spawnProcess(sessionKey, log) {
|
|
177
|
+
const args = this.buildArgs(sessionKey);
|
|
178
|
+
const env = buildSpawnEnv(process.env, this.opts.claudeHome, this.buildPlaceholderContext(sessionKey), { allowApiKey: this.opts.allowApiKey, effortLevel: this._effortLevel });
|
|
179
|
+
log?.info(`claude-agent: spawn long-lived ${this.opts.claudeBin} (session ${sessionKey}, model=${this._model || "default"}, effort=${this._effortLevel || "default"}, mode=${this.opts.permissionMode})`);
|
|
180
|
+
const proc = spawn(this.opts.claudeBin, args, {
|
|
181
|
+
cwd: this.opts.workspaceDir,
|
|
182
|
+
env,
|
|
183
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
184
|
+
});
|
|
185
|
+
if (!proc.stdout || !proc.stderr || !proc.stdin) {
|
|
186
|
+
throw new Error("Claude subprocess did not provide stdio pipes");
|
|
187
|
+
}
|
|
188
|
+
const stderrChunks = [];
|
|
189
|
+
proc.stderr.on("data", (chunk) => {
|
|
190
|
+
stderrChunks.push(chunk.toString());
|
|
191
|
+
});
|
|
192
|
+
proc.stdin.on("error", () => {
|
|
193
|
+
// Absorb async EPIPE / ERR_STREAM_WRITE_AFTER_END when the child
|
|
194
|
+
// exits between our write check and the kernel delivering the data.
|
|
195
|
+
// The next parser.next() will see the stream end and surface an error
|
|
196
|
+
// RuntimeEvent through the normal turn-end path.
|
|
197
|
+
});
|
|
198
|
+
const exitPromise = new Promise((resolve) => {
|
|
199
|
+
proc.once("close", (code, signal) => resolve({ code, signal }));
|
|
200
|
+
proc.once("error", () => resolve({ code: null, signal: null }));
|
|
201
|
+
});
|
|
202
|
+
return { proc, exitPromise, stderrChunks };
|
|
203
|
+
}
|
|
204
|
+
killProcess(sessionKey, state) {
|
|
205
|
+
state.done = true;
|
|
206
|
+
const current = this.processes.get(sessionKey);
|
|
207
|
+
if (current === state) {
|
|
208
|
+
this.processes.delete(sessionKey);
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
state.handle.proc.stdin.end();
|
|
212
|
+
}
|
|
213
|
+
catch { /* best-effort */ }
|
|
214
|
+
if (state.handle.proc.exitCode === null && state.handle.proc.signalCode === null) {
|
|
215
|
+
try {
|
|
216
|
+
state.handle.proc.kill("SIGTERM");
|
|
217
|
+
}
|
|
218
|
+
catch { /* best-effort */ }
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
writeUserMessage(handle, text) {
|
|
222
|
+
const payload = JSON.stringify({
|
|
223
|
+
type: "user",
|
|
224
|
+
message: {
|
|
225
|
+
role: "user",
|
|
226
|
+
content: [{ type: "text", text }],
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
handle.proc.stdin.write(`${payload}\n`);
|
|
230
|
+
}
|
|
231
|
+
buildArgs(sessionKey) {
|
|
128
232
|
const args = [
|
|
129
|
-
"
|
|
233
|
+
"-p",
|
|
130
234
|
"--verbose",
|
|
235
|
+
"--input-format",
|
|
236
|
+
"stream-json",
|
|
131
237
|
"--output-format",
|
|
132
238
|
"stream-json",
|
|
133
239
|
"--permission-mode",
|
|
134
240
|
this.opts.permissionMode,
|
|
135
241
|
];
|
|
136
|
-
if (this.
|
|
137
|
-
args.push("--model", this.
|
|
242
|
+
if (this._model) {
|
|
243
|
+
args.push("--model", this._model);
|
|
138
244
|
}
|
|
139
245
|
if (this.opts.allowedTools.length > 0) {
|
|
140
246
|
args.push("--allowedTools", this.opts.allowedTools.join(","));
|
|
@@ -142,6 +248,7 @@ export class ClaudeCodeAdapter {
|
|
|
142
248
|
if (this.opts.disallowedTools.length > 0) {
|
|
143
249
|
args.push("--disallowedTools", this.opts.disallowedTools.join(","));
|
|
144
250
|
}
|
|
251
|
+
args.push("--append-system-prompt-file", path.join(this.opts.workspaceDir, ".parall", "system-prompt.md"));
|
|
145
252
|
if (this.opts.appendSystemPrompt) {
|
|
146
253
|
args.push("--append-system-prompt", this.opts.appendSystemPrompt);
|
|
147
254
|
}
|
|
@@ -149,7 +256,24 @@ export class ClaudeCodeAdapter {
|
|
|
149
256
|
args.push("--add-dir", ...this.opts.additionalDirs);
|
|
150
257
|
}
|
|
151
258
|
args.push(...this.opts.sessionManager.getResumeArgs(sessionKey));
|
|
152
|
-
args.push(prompt);
|
|
153
259
|
return args;
|
|
154
260
|
}
|
|
261
|
+
buildPlaceholderContext(sessionKey) {
|
|
262
|
+
return {
|
|
263
|
+
accountId: "",
|
|
264
|
+
apiUrl: this.opts.apiUrl,
|
|
265
|
+
apiKey: this.opts.apiKey,
|
|
266
|
+
orgId: this.opts.orgId,
|
|
267
|
+
agentUserId: "",
|
|
268
|
+
runtimeType: "",
|
|
269
|
+
runtimeKey: "",
|
|
270
|
+
sessionId: "",
|
|
271
|
+
chatId: "",
|
|
272
|
+
triggerMessageId: "",
|
|
273
|
+
noReply: false,
|
|
274
|
+
contextFilePath: this.opts.contextFilePathForSession?.(sessionKey) ?? "",
|
|
275
|
+
stepIdFilePath: this.opts.stepIdFilePathForSession?.(sessionKey) ?? "",
|
|
276
|
+
client: undefined,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
155
279
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as os from "node:os";
|
|
3
|
-
import { ParallAgentGateway, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
3
|
+
import { ParallAgentGateway, createPlatformConfigManager, isPlatformManagedProfile, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
4
4
|
import { ApiError, ParallClient, ParallWs } from "@parall/sdk";
|
|
5
|
-
import { buildClaudeRuntimeKey, resolveClaudeAgentConfig, resolveWsUrl, sessionStateFilePathForRuntime, stepIdFilePathForSession, } from "./config.js";
|
|
5
|
+
import { buildClaudeRuntimeKey, contextFilePathForSession, resolveClaudeAgentConfig, resolveWsUrl, sessionStateFilePathForRuntime, stepIdFilePathForSession, } from "./config.js";
|
|
6
6
|
import { ClaudeCodeAdapter } from "./dispatch.js";
|
|
7
7
|
import { ClaudeSessionManager } from "./session-manager.js";
|
|
8
8
|
import { ensureClaudeWorkspace } from "./workspace.js";
|
|
@@ -55,11 +55,22 @@ async function main() {
|
|
|
55
55
|
getTicket: () => client.getWsTicket(),
|
|
56
56
|
wsUrl: resolvedWsUrl,
|
|
57
57
|
});
|
|
58
|
+
const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "claude-code", log });
|
|
59
|
+
const platformDefaults = await configMgr.fetch();
|
|
60
|
+
let platformManaged = isPlatformManagedProfile(me.agent_profile);
|
|
61
|
+
const resolvedModel = platformManaged ? (platformDefaults.model ?? config.model) : config.model;
|
|
62
|
+
const resolvedEffort = platformManaged
|
|
63
|
+
? (platformDefaults.thinkingEffort ?? (process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || undefined))
|
|
64
|
+
: (process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || undefined);
|
|
65
|
+
if (platformDefaults.model)
|
|
66
|
+
log.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
67
|
+
if (platformDefaults.thinkingEffort)
|
|
68
|
+
log.info(`platform config: thinking_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
58
69
|
const adapter = new ClaudeCodeAdapter({
|
|
59
70
|
claudeBin: config.claudeBin,
|
|
60
71
|
claudeHome: config.claudeHome,
|
|
61
72
|
workspaceDir: config.workspaceDir,
|
|
62
|
-
model:
|
|
73
|
+
model: resolvedModel,
|
|
63
74
|
permissionMode: config.permissionMode,
|
|
64
75
|
allowedTools: config.allowedTools,
|
|
65
76
|
disallowedTools: config.disallowedTools,
|
|
@@ -67,7 +78,18 @@ async function main() {
|
|
|
67
78
|
appendSystemPrompt: config.appendSystemPrompt,
|
|
68
79
|
allowApiKey: config.allowApiKey,
|
|
69
80
|
sessionManager,
|
|
81
|
+
// Static Parall credentials. The long-lived subprocess must authenticate
|
|
82
|
+
// against Parall's API across many dispatches, so these cannot come from
|
|
83
|
+
// a per-dispatch DispatchContext.
|
|
84
|
+
apiUrl: config.apiUrl,
|
|
85
|
+
apiKey: config.apiKey,
|
|
86
|
+
orgId: config.orgId,
|
|
87
|
+
contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
|
|
88
|
+
stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
|
|
70
89
|
});
|
|
90
|
+
if (resolvedEffort) {
|
|
91
|
+
adapter.updateConfig({ effort: resolvedEffort });
|
|
92
|
+
}
|
|
71
93
|
const gateway = new ParallAgentGateway({
|
|
72
94
|
accountId: agentUserId,
|
|
73
95
|
client,
|
|
@@ -90,7 +112,33 @@ async function main() {
|
|
|
90
112
|
dispatchAdapter: adapter,
|
|
91
113
|
log,
|
|
92
114
|
shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
|
|
115
|
+
contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
|
|
93
116
|
stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
|
|
117
|
+
onConfigUpdate: async () => {
|
|
118
|
+
const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
|
|
119
|
+
platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
|
|
120
|
+
const updated = await configMgr.fetch();
|
|
121
|
+
const localEffort = process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || null;
|
|
122
|
+
adapter.updateConfig({
|
|
123
|
+
model: platformManaged ? (updated.model ?? config.model) : (config.model ?? null),
|
|
124
|
+
effort: platformManaged ? (updated.thinkingEffort ?? localEffort) : localEffort,
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
onSessionReady: async () => {
|
|
128
|
+
const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
|
|
129
|
+
platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
|
|
130
|
+
const updated = await configMgr.fetch();
|
|
131
|
+
const localEffort = process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || null;
|
|
132
|
+
adapter.updateConfig({
|
|
133
|
+
model: platformManaged ? (updated.model ?? config.model) : (config.model ?? null),
|
|
134
|
+
effort: platformManaged ? (updated.thinkingEffort ?? localEffort) : localEffort,
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
// Long-lived `claude` subprocesses outlive a single dispatch, so the
|
|
138
|
+
// gateway's shuttingDown flag alone does not tear them down. Piggyback on
|
|
139
|
+
// onBeforeDisconnect to close stdin and SIGTERM any survivors after
|
|
140
|
+
// in-flight drains finish.
|
|
141
|
+
onBeforeDisconnect: () => adapter.shutdown(),
|
|
94
142
|
});
|
|
95
143
|
const abortController = new AbortController();
|
|
96
144
|
const abort = () => abortController.abort();
|
package/dist/output-parser.d.ts
CHANGED
|
@@ -3,6 +3,10 @@ import type { RuntimeEvent } from "@parall/agent-core";
|
|
|
3
3
|
export type ClaudeParsedEvent = RuntimeEvent | {
|
|
4
4
|
type: "session_id";
|
|
5
5
|
sessionId: string;
|
|
6
|
+
} | {
|
|
7
|
+
type: "turn_end";
|
|
8
|
+
sessionId?: string;
|
|
9
|
+
isError: boolean;
|
|
6
10
|
};
|
|
7
11
|
export declare function parseClaudeStreamJson(readable: Readable): AsyncGenerator<ClaudeParsedEvent>;
|
|
8
12
|
//# sourceMappingURL=output-parser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output-parser.d.ts","sourceRoot":"","sources":["../src/output-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"output-parser.d.ts","sourceRoot":"","sources":["../src/output-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAWzC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AA4E/D,wBAAuB,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc,CAAC,iBAAiB,CAAC,CA8HlG"}
|
package/dist/output-parser.js
CHANGED
|
@@ -173,11 +173,25 @@ export async function* parseClaudeStreamJson(readable) {
|
|
|
173
173
|
}
|
|
174
174
|
continue;
|
|
175
175
|
}
|
|
176
|
-
if (eventRecord.type === "result"
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
176
|
+
if (eventRecord.type === "result") {
|
|
177
|
+
const isError = eventRecord.is_error === true;
|
|
178
|
+
if (isError) {
|
|
179
|
+
const message = asTrimmedString(eventRecord.result)
|
|
180
|
+
|| asTrimmedString(eventRecord.error)
|
|
181
|
+
|| "Claude dispatch failed";
|
|
182
|
+
yield { type: "error", message };
|
|
183
|
+
}
|
|
184
|
+
// Turn boundary: the subprocess lives across many turns, so any
|
|
185
|
+
// tool_use entries that never matched a tool_result this turn
|
|
186
|
+
// (tool crashed mid-call, Claude aborted, etc.) must not leak into
|
|
187
|
+
// the next turn's accounting. Drop them here rather than letting
|
|
188
|
+
// the map grow unbounded or mislabel durations on id collisions.
|
|
189
|
+
toolUses.clear();
|
|
190
|
+
yield {
|
|
191
|
+
type: "turn_end",
|
|
192
|
+
sessionId: asTrimmedString(eventRecord.session_id),
|
|
193
|
+
isError,
|
|
194
|
+
};
|
|
181
195
|
}
|
|
182
196
|
}
|
|
183
197
|
}
|
|
@@ -1,18 +1,41 @@
|
|
|
1
|
+
import type { ChildProcessWithoutNullStreams } from "node:child_process";
|
|
1
2
|
import type { ForkSessionHandle } from "@parall/agent-core";
|
|
2
3
|
type ClaudeSessionManagerLogger = {
|
|
3
4
|
warn(message: string): void;
|
|
4
5
|
};
|
|
6
|
+
export type ClaudeProcessHandle = {
|
|
7
|
+
proc: ChildProcessWithoutNullStreams;
|
|
8
|
+
exitPromise: Promise<{
|
|
9
|
+
code: number | null;
|
|
10
|
+
signal: NodeJS.Signals | null;
|
|
11
|
+
}>;
|
|
12
|
+
stderrChunks: string[];
|
|
13
|
+
};
|
|
5
14
|
export declare class ClaudeSessionManager {
|
|
6
15
|
private readonly mainSessionKey;
|
|
7
16
|
private readonly stateFilePath;
|
|
8
17
|
private readonly logger?;
|
|
9
18
|
private readonly sessionIds;
|
|
10
19
|
private readonly pendingForkParents;
|
|
20
|
+
private readonly processes;
|
|
11
21
|
constructor(mainSessionKey: string, stateFilePath: string, logger?: ClaudeSessionManagerLogger | undefined);
|
|
12
22
|
getResumeArgs(sessionKey: string): string[];
|
|
13
23
|
recordSessionId(sessionKey: string, sessionId: string): void;
|
|
14
24
|
createForkSession(parentSessionKey: string): ForkSessionHandle | null;
|
|
15
25
|
cleanupFork(sessionKey: string): void;
|
|
26
|
+
getProcess(sessionKey: string): ClaudeProcessHandle | undefined;
|
|
27
|
+
registerProcess(sessionKey: string, handle: ClaudeProcessHandle): void;
|
|
28
|
+
clearProcess(sessionKey: string, handle?: ClaudeProcessHandle): void;
|
|
29
|
+
/** Grace period (ms) between SIGTERM and SIGKILL during shutdown. */
|
|
30
|
+
private static readonly SHUTDOWN_GRACE_MS;
|
|
31
|
+
shutdownAll(): Promise<void>;
|
|
32
|
+
private shutdownOne;
|
|
33
|
+
/**
|
|
34
|
+
* Wait up to `ms` for `promise`. Resolves `false` if the promise settled
|
|
35
|
+
* in time, `true` if the timeout fired first. Never rejects.
|
|
36
|
+
*/
|
|
37
|
+
private raceWithTimeout;
|
|
38
|
+
private closeHandle;
|
|
16
39
|
private restore;
|
|
17
40
|
private persist;
|
|
18
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAO5D,KAAK,0BAA0B,GAAG;IAChC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,qBAAa,oBAAoB;
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAO5D,KAAK,0BAA0B,GAAG;IAChC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,8BAA8B,CAAC;IACrC,WAAW,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAC7E,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,qBAAa,oBAAoB;IAM7B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAP1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IACxD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA6B;IAChE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0C;gBAGjD,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,0BAA0B,YAAA;IAKtD,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAU3C,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAQrD,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAYrE,WAAW,CAAC,UAAU,EAAE,MAAM;IAU9B,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAI/D,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB;IAsB/D,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,mBAAmB;IAO7D,qEAAqE;IACrE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE5C,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;YAQpB,WAAW;IAuCzB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAuBvB,OAAO,CAAC,WAAW;IAqBnB,OAAO,CAAC,OAAO;IAYf,OAAO,CAAC,OAAO;CAahB"}
|