@kodizm/acp 0.8.2 → 0.9.1
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/index.js +1193 -415
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -46,6 +46,75 @@ var __esm = (fn, res, err) => () => {
|
|
|
46
46
|
};
|
|
47
47
|
var __require = import.meta.require;
|
|
48
48
|
|
|
49
|
+
// package.json
|
|
50
|
+
var package_default;
|
|
51
|
+
var init_package = __esm(() => {
|
|
52
|
+
package_default = {
|
|
53
|
+
name: "@kodizm/acp",
|
|
54
|
+
version: "0.9.1",
|
|
55
|
+
description: "Custom ACP server bridging Claude Code and opencode CLIs through a single Kodizm-flavored protocol surface.",
|
|
56
|
+
type: "module",
|
|
57
|
+
bin: {
|
|
58
|
+
"kodizm-acp": "./dist/index.js"
|
|
59
|
+
},
|
|
60
|
+
main: "./dist/index.js",
|
|
61
|
+
files: [
|
|
62
|
+
"dist/",
|
|
63
|
+
"README.md",
|
|
64
|
+
"LICENSE"
|
|
65
|
+
],
|
|
66
|
+
scripts: {
|
|
67
|
+
dev: "bun run src/index.ts",
|
|
68
|
+
build: "bun build src/index.ts --target=bun --outdir=dist",
|
|
69
|
+
test: "bun test test/unit test/e2e",
|
|
70
|
+
"test:unit": "bun test test/unit",
|
|
71
|
+
"test:e2e": "bun test test/e2e",
|
|
72
|
+
"test:integration": "./scripts/reap-opencode.sh bun test test/integration",
|
|
73
|
+
"test:watch": "bun test --watch",
|
|
74
|
+
lint: "biome check src test",
|
|
75
|
+
"lint:fix": "biome check --write src test",
|
|
76
|
+
format: "biome format --write src test",
|
|
77
|
+
typecheck: "tsc --noEmit",
|
|
78
|
+
"typecheck:src": "tsc --noEmit -p tsconfig.publish.json",
|
|
79
|
+
prepublishOnly: "bun run build && bun run typecheck:src"
|
|
80
|
+
},
|
|
81
|
+
engines: {
|
|
82
|
+
bun: ">=1.1.0"
|
|
83
|
+
},
|
|
84
|
+
publishConfig: {
|
|
85
|
+
access: "public"
|
|
86
|
+
},
|
|
87
|
+
config: {
|
|
88
|
+
claudeAgentSdk: "0.3.280"
|
|
89
|
+
},
|
|
90
|
+
devDependencies: {
|
|
91
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.280",
|
|
92
|
+
"@anthropic-ai/sdk": ">=0.93.0",
|
|
93
|
+
"@biomejs/biome": "^1.9.0",
|
|
94
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
95
|
+
"@opencode-ai/sdk": "^1.18.32",
|
|
96
|
+
"@types/bun": "latest",
|
|
97
|
+
typescript: "^5.6.0",
|
|
98
|
+
zod: "^4.0.0"
|
|
99
|
+
},
|
|
100
|
+
license: "Apache-2.0",
|
|
101
|
+
homepage: "https://github.com/kodizm/acp",
|
|
102
|
+
repository: {
|
|
103
|
+
type: "git",
|
|
104
|
+
url: "git+https://github.com/kodizm/acp.git"
|
|
105
|
+
},
|
|
106
|
+
keywords: [
|
|
107
|
+
"acp",
|
|
108
|
+
"agent-client-protocol",
|
|
109
|
+
"claude",
|
|
110
|
+
"opencode",
|
|
111
|
+
"ai",
|
|
112
|
+
"bridge",
|
|
113
|
+
"jsonrpc"
|
|
114
|
+
]
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
|
|
49
118
|
// src/server/errors.ts
|
|
50
119
|
var JsonRpcErrorCode, JsonRpcError, InvalidParamsError, SessionNotFoundError, MethodNotSupportedError, AcpTimeoutError, BackendNotConfiguredError, UnknownBackendError;
|
|
51
120
|
var init_errors = __esm(() => {
|
|
@@ -4983,6 +5052,151 @@ var init_zod = __esm(() => {
|
|
|
4983
5052
|
init_external();
|
|
4984
5053
|
});
|
|
4985
5054
|
|
|
5055
|
+
// src/wire/schemas.ts
|
|
5056
|
+
function metaCarriesNoCanonicalFields(data) {
|
|
5057
|
+
if (data._meta === undefined) {
|
|
5058
|
+
return true;
|
|
5059
|
+
}
|
|
5060
|
+
for (const banned of CANONICAL_FIELDS_BANNED_IN_META) {
|
|
5061
|
+
if (banned in data._meta) {
|
|
5062
|
+
return false;
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5065
|
+
return true;
|
|
5066
|
+
}
|
|
5067
|
+
function timeoutsAreExclusive(data) {
|
|
5068
|
+
return !(data.permissionTimeoutMs !== undefined && data.permissionDeferTimeoutMs !== undefined);
|
|
5069
|
+
}
|
|
5070
|
+
var AbsolutePathSchema, PLUGIN_ROOT = "/opt/kodizm/plugins/", PluginPathSchema, McpServerSchema, SystemPromptSchema, MetaSchema, CANONICAL_FIELDS_BANNED_IN_META, SettingSourcesSchema, PermissionModeSchema, ToolPolicySchema, FeaturesSchema, META_SMUGGLE_MESSAGE = "canonical fields (systemPrompt, additionalDirectories, mcpServers, model, skills, cwd) must be top-level, not nested in _meta", InitializeRequestSchema, SessionOptionFields, TIMEOUT_EXCLUSION, NewSessionRequestSchema, PromptRequestSchema, CancelRequestSchema, LoadSessionRequestSchema, CompactSessionRequestSchema, ForkSessionRequestSchema;
|
|
5071
|
+
var init_schemas3 = __esm(() => {
|
|
5072
|
+
init_zod();
|
|
5073
|
+
AbsolutePathSchema = string2().regex(/^\//, 'path must be absolute (start with "/")');
|
|
5074
|
+
PluginPathSchema = AbsolutePathSchema.refine((path) => path.length > PLUGIN_ROOT.length && path.startsWith(PLUGIN_ROOT) && !path.split("/").includes(".."), { message: `plugin path must live under ${PLUGIN_ROOT} with no ".." segment` });
|
|
5075
|
+
McpServerSchema = object({
|
|
5076
|
+
type: literal("http"),
|
|
5077
|
+
name: string2().min(1),
|
|
5078
|
+
url: string2().url(),
|
|
5079
|
+
headers: array(object({
|
|
5080
|
+
name: string2().min(1),
|
|
5081
|
+
value: string2()
|
|
5082
|
+
})).optional()
|
|
5083
|
+
});
|
|
5084
|
+
SystemPromptSchema = union([
|
|
5085
|
+
string2(),
|
|
5086
|
+
object({
|
|
5087
|
+
append: string2(),
|
|
5088
|
+
excludeDynamicSections: boolean2().optional()
|
|
5089
|
+
})
|
|
5090
|
+
]);
|
|
5091
|
+
MetaSchema = record(string2(), unknown());
|
|
5092
|
+
CANONICAL_FIELDS_BANNED_IN_META = [
|
|
5093
|
+
"systemPrompt",
|
|
5094
|
+
"additionalDirectories",
|
|
5095
|
+
"mcpServers",
|
|
5096
|
+
"model",
|
|
5097
|
+
"skills",
|
|
5098
|
+
"cwd",
|
|
5099
|
+
"sessionId",
|
|
5100
|
+
"sourceSessionId",
|
|
5101
|
+
"toolPolicy",
|
|
5102
|
+
"autoCompact",
|
|
5103
|
+
"permissionTimeoutMs",
|
|
5104
|
+
"permissionDeferTimeoutMs",
|
|
5105
|
+
"debug",
|
|
5106
|
+
"debugCaptureRawSdk",
|
|
5107
|
+
"debugCaptureRpc",
|
|
5108
|
+
"heartbeatIntervalMs",
|
|
5109
|
+
"inactivityThresholdMs",
|
|
5110
|
+
"settingSources",
|
|
5111
|
+
"features",
|
|
5112
|
+
"plugins"
|
|
5113
|
+
];
|
|
5114
|
+
SettingSourcesSchema = array(_enum(["user", "project", "local"]));
|
|
5115
|
+
PermissionModeSchema = _enum(["default", "acceptEdits", "plan", "dontAsk", "bypassPermissions"]);
|
|
5116
|
+
ToolPolicySchema = object({
|
|
5117
|
+
allow: array(string2()).optional(),
|
|
5118
|
+
deny: array(string2()).optional(),
|
|
5119
|
+
ask: array(string2()).optional(),
|
|
5120
|
+
defaultMode: PermissionModeSchema.optional()
|
|
5121
|
+
});
|
|
5122
|
+
FeaturesSchema = object({
|
|
5123
|
+
todos: boolean2().optional(),
|
|
5124
|
+
subagents: boolean2().optional(),
|
|
5125
|
+
backgroundTasks: boolean2().optional(),
|
|
5126
|
+
scheduling: boolean2().optional(),
|
|
5127
|
+
planMode: boolean2().optional(),
|
|
5128
|
+
worktree: boolean2().optional(),
|
|
5129
|
+
notebook: boolean2().optional(),
|
|
5130
|
+
clientTools: boolean2().optional(),
|
|
5131
|
+
autoMemory: boolean2().optional(),
|
|
5132
|
+
bundledSkills: boolean2().optional(),
|
|
5133
|
+
simple: boolean2().optional(),
|
|
5134
|
+
tools: array(string2().min(1)).optional()
|
|
5135
|
+
});
|
|
5136
|
+
InitializeRequestSchema = object({
|
|
5137
|
+
protocolVersion: number2().int().positive(),
|
|
5138
|
+
clientCapabilities: record(string2(), unknown()).optional(),
|
|
5139
|
+
_meta: MetaSchema.optional()
|
|
5140
|
+
});
|
|
5141
|
+
SessionOptionFields = {
|
|
5142
|
+
additionalDirectories: array(AbsolutePathSchema).optional(),
|
|
5143
|
+
plugins: array(PluginPathSchema).optional(),
|
|
5144
|
+
systemPrompt: SystemPromptSchema.optional(),
|
|
5145
|
+
model: string2().optional(),
|
|
5146
|
+
skills: array(string2()).optional(),
|
|
5147
|
+
toolPolicy: ToolPolicySchema.optional(),
|
|
5148
|
+
autoCompact: boolean2().optional(),
|
|
5149
|
+
permissionTimeoutMs: number2().int().positive().optional(),
|
|
5150
|
+
permissionDeferTimeoutMs: number2().int().positive().optional(),
|
|
5151
|
+
debug: boolean2().optional(),
|
|
5152
|
+
debugCaptureRawSdk: boolean2().optional(),
|
|
5153
|
+
debugCaptureRpc: boolean2().optional(),
|
|
5154
|
+
heartbeatIntervalMs: number2().int().positive().optional(),
|
|
5155
|
+
inactivityThresholdMs: number2().int().positive().optional(),
|
|
5156
|
+
settingSources: SettingSourcesSchema.optional(),
|
|
5157
|
+
features: FeaturesSchema.optional(),
|
|
5158
|
+
_meta: MetaSchema.optional()
|
|
5159
|
+
};
|
|
5160
|
+
TIMEOUT_EXCLUSION = {
|
|
5161
|
+
message: "permissionTimeoutMs and permissionDeferTimeoutMs are mutually exclusive (pick hard-deny on timeout OR soft-defer on timeout)",
|
|
5162
|
+
path: ["permissionDeferTimeoutMs"]
|
|
5163
|
+
};
|
|
5164
|
+
NewSessionRequestSchema = object({
|
|
5165
|
+
cwd: AbsolutePathSchema,
|
|
5166
|
+
mcpServers: array(McpServerSchema),
|
|
5167
|
+
...SessionOptionFields
|
|
5168
|
+
}).refine(metaCarriesNoCanonicalFields, { message: META_SMUGGLE_MESSAGE, path: ["_meta"] }).refine(timeoutsAreExclusive, TIMEOUT_EXCLUSION);
|
|
5169
|
+
PromptRequestSchema = object({
|
|
5170
|
+
sessionId: string2().min(1),
|
|
5171
|
+
prompt: array(unknown()),
|
|
5172
|
+
model: string2().optional(),
|
|
5173
|
+
_meta: MetaSchema.optional()
|
|
5174
|
+
}).refine(metaCarriesNoCanonicalFields, { message: META_SMUGGLE_MESSAGE, path: ["_meta"] });
|
|
5175
|
+
CancelRequestSchema = object({
|
|
5176
|
+
sessionId: string2().min(1)
|
|
5177
|
+
});
|
|
5178
|
+
LoadSessionRequestSchema = object({
|
|
5179
|
+
sessionId: string2().min(1),
|
|
5180
|
+
cwd: AbsolutePathSchema,
|
|
5181
|
+
mcpServers: array(McpServerSchema),
|
|
5182
|
+
...SessionOptionFields
|
|
5183
|
+
}).refine(metaCarriesNoCanonicalFields, { message: META_SMUGGLE_MESSAGE, path: ["_meta"] }).refine(timeoutsAreExclusive, TIMEOUT_EXCLUSION);
|
|
5184
|
+
CompactSessionRequestSchema = object({
|
|
5185
|
+
sessionId: string2().min(1)
|
|
5186
|
+
});
|
|
5187
|
+
ForkSessionRequestSchema = object({
|
|
5188
|
+
sourceSessionId: string2().min(1),
|
|
5189
|
+
cwd: AbsolutePathSchema,
|
|
5190
|
+
mcpServers: array(McpServerSchema),
|
|
5191
|
+
systemPrompt: SystemPromptSchema.optional(),
|
|
5192
|
+
model: string2().optional(),
|
|
5193
|
+
additionalDirectories: array(AbsolutePathSchema).optional(),
|
|
5194
|
+
settingSources: SettingSourcesSchema.optional(),
|
|
5195
|
+
features: FeaturesSchema.optional(),
|
|
5196
|
+
_meta: MetaSchema.optional()
|
|
5197
|
+
}).refine(metaCarriesNoCanonicalFields, { message: META_SMUGGLE_MESSAGE, path: ["_meta"] });
|
|
5198
|
+
});
|
|
5199
|
+
|
|
4986
5200
|
// src/server/heartbeat.ts
|
|
4987
5201
|
class HeartbeatTimer {
|
|
4988
5202
|
options;
|
|
@@ -5206,58 +5420,7 @@ var init_events = __esm(() => {
|
|
|
5206
5420
|
]);
|
|
5207
5421
|
});
|
|
5208
5422
|
|
|
5209
|
-
// src/backends/
|
|
5210
|
-
function buildCanUseTool(deps) {
|
|
5211
|
-
return async function canUseTool(toolName, input, options) {
|
|
5212
|
-
const payload = {
|
|
5213
|
-
sessionId: deps.sessionId,
|
|
5214
|
-
toolCall: {
|
|
5215
|
-
toolCallId: options.toolUseID,
|
|
5216
|
-
rawInput: input,
|
|
5217
|
-
title: options.title ?? toolName,
|
|
5218
|
-
kind: "tool_use"
|
|
5219
|
-
},
|
|
5220
|
-
options: DEFAULT_OPTIONS
|
|
5221
|
-
};
|
|
5222
|
-
if (options.agentID !== undefined) {
|
|
5223
|
-
payload.agentId = options.agentID;
|
|
5224
|
-
}
|
|
5225
|
-
const event = {
|
|
5226
|
-
sessionId: deps.sessionId,
|
|
5227
|
-
type: "permission_request",
|
|
5228
|
-
toolUseId: options.toolUseID,
|
|
5229
|
-
name: toolName,
|
|
5230
|
-
options: DEFAULT_OPTIONS.map((o) => ({ optionId: o.optionId, label: o.name })),
|
|
5231
|
-
...options.agentID === undefined ? {} : { agentId: options.agentID, parentSessionId: deps.sessionId }
|
|
5232
|
-
};
|
|
5233
|
-
deps.emit.send(event);
|
|
5234
|
-
const raceOptions = {
|
|
5235
|
-
signal: composeSignals(deps.signal, options.signal)
|
|
5236
|
-
};
|
|
5237
|
-
if (deps.permissionTimeoutMs !== undefined) {
|
|
5238
|
-
raceOptions.timeoutMs = deps.permissionTimeoutMs;
|
|
5239
|
-
}
|
|
5240
|
-
if (deps.deferTimeoutMs !== undefined && deps.onDefer !== undefined) {
|
|
5241
|
-
raceOptions.deferTimeoutMs = deps.deferTimeoutMs;
|
|
5242
|
-
}
|
|
5243
|
-
let raced;
|
|
5244
|
-
try {
|
|
5245
|
-
raced = await awaitPermissionResponse(deps.server, "session/request_permission", payload, raceOptions);
|
|
5246
|
-
} catch (error) {
|
|
5247
|
-
if (error instanceof AcpTimeoutError) {
|
|
5248
|
-
return { behavior: "deny", message: "Permission RPC timed out" };
|
|
5249
|
-
}
|
|
5250
|
-
throw error;
|
|
5251
|
-
}
|
|
5252
|
-
if (raced === DEFERRED_SENTINEL) {
|
|
5253
|
-
if (deps.onDefer === undefined) {
|
|
5254
|
-
return { behavior: "deny", message: "Permission deferred (no handler)" };
|
|
5255
|
-
}
|
|
5256
|
-
return await deps.onDefer({ toolName, input, options });
|
|
5257
|
-
}
|
|
5258
|
-
return mapPermissionOutcome(raced, input, options.suggestions);
|
|
5259
|
-
};
|
|
5260
|
-
}
|
|
5423
|
+
// src/backends/permission-rpc.ts
|
|
5261
5424
|
async function awaitPermissionResponse(server, method, params, options = {}) {
|
|
5262
5425
|
const racers = [server.request(method, params)];
|
|
5263
5426
|
let timeoutHandle;
|
|
@@ -5304,49 +5467,9 @@ async function awaitPermissionResponse(server, method, params, options = {}) {
|
|
|
5304
5467
|
}
|
|
5305
5468
|
}
|
|
5306
5469
|
}
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
return a;
|
|
5310
|
-
if (b.aborted)
|
|
5311
|
-
return b;
|
|
5312
|
-
const controller = new AbortController;
|
|
5313
|
-
const onAbort = () => controller.abort();
|
|
5314
|
-
a.addEventListener("abort", onAbort, { once: true });
|
|
5315
|
-
b.addEventListener("abort", onAbort, { once: true });
|
|
5316
|
-
return controller.signal;
|
|
5317
|
-
}
|
|
5318
|
-
function mapPermissionOutcome(response, input, suggestions) {
|
|
5319
|
-
const outcome = response.outcome;
|
|
5320
|
-
if (outcome.outcome === "cancelled") {
|
|
5321
|
-
throw new Error("Tool use aborted");
|
|
5322
|
-
}
|
|
5323
|
-
if (outcome.optionId === "allow") {
|
|
5324
|
-
return { behavior: "allow", updatedInput: input };
|
|
5325
|
-
}
|
|
5326
|
-
if (outcome.optionId === "allow_always") {
|
|
5327
|
-
return {
|
|
5328
|
-
behavior: "allow",
|
|
5329
|
-
updatedInput: input,
|
|
5330
|
-
updatedPermissions: suggestions !== undefined && suggestions.length > 0 ? suggestions : [
|
|
5331
|
-
{
|
|
5332
|
-
type: "addRules",
|
|
5333
|
-
rules: [{ toolName: "unknown" }],
|
|
5334
|
-
behavior: "allow",
|
|
5335
|
-
destination: "session"
|
|
5336
|
-
}
|
|
5337
|
-
]
|
|
5338
|
-
};
|
|
5339
|
-
}
|
|
5340
|
-
return { behavior: "deny", message: "User refused permission to run tool" };
|
|
5341
|
-
}
|
|
5342
|
-
var DEFAULT_OPTIONS, DEFERRED_SENTINEL;
|
|
5343
|
-
var init_permission_bridge = __esm(() => {
|
|
5470
|
+
var DEFERRED_SENTINEL;
|
|
5471
|
+
var init_permission_rpc = __esm(() => {
|
|
5344
5472
|
init_errors();
|
|
5345
|
-
DEFAULT_OPTIONS = [
|
|
5346
|
-
{ kind: "allow_always", name: "Always allow this tool", optionId: "allow_always" },
|
|
5347
|
-
{ kind: "allow_once", name: "Allow", optionId: "allow" },
|
|
5348
|
-
{ kind: "reject_once", name: "Reject", optionId: "reject" }
|
|
5349
|
-
];
|
|
5350
5473
|
DEFERRED_SENTINEL = Symbol.for("kodizm.permission.deferred");
|
|
5351
5474
|
});
|
|
5352
5475
|
|
|
@@ -5399,7 +5522,7 @@ var QuestionsArraySchema;
|
|
|
5399
5522
|
var init_ask_user_question = __esm(() => {
|
|
5400
5523
|
init_zod();
|
|
5401
5524
|
init_events();
|
|
5402
|
-
|
|
5525
|
+
init_permission_rpc();
|
|
5403
5526
|
QuestionsArraySchema = array(KodizmQuestionSchema).min(1).max(4);
|
|
5404
5527
|
});
|
|
5405
5528
|
|
|
@@ -5784,6 +5907,104 @@ function buildSdkServer(server) {
|
|
|
5784
5907
|
return sdkServer;
|
|
5785
5908
|
}
|
|
5786
5909
|
|
|
5910
|
+
// src/backends/claude/permission-bridge.ts
|
|
5911
|
+
function buildCanUseTool(deps) {
|
|
5912
|
+
return async function canUseTool(toolName, input, options) {
|
|
5913
|
+
const payload = {
|
|
5914
|
+
sessionId: deps.sessionId,
|
|
5915
|
+
toolCall: {
|
|
5916
|
+
toolCallId: options.toolUseID,
|
|
5917
|
+
rawInput: input,
|
|
5918
|
+
title: options.title ?? toolName,
|
|
5919
|
+
kind: "tool_use"
|
|
5920
|
+
},
|
|
5921
|
+
options: DEFAULT_OPTIONS
|
|
5922
|
+
};
|
|
5923
|
+
if (options.agentID !== undefined) {
|
|
5924
|
+
payload.agentId = options.agentID;
|
|
5925
|
+
}
|
|
5926
|
+
const event = {
|
|
5927
|
+
sessionId: deps.sessionId,
|
|
5928
|
+
type: "permission_request",
|
|
5929
|
+
toolUseId: options.toolUseID,
|
|
5930
|
+
name: toolName,
|
|
5931
|
+
options: DEFAULT_OPTIONS.map((o) => ({ optionId: o.optionId, label: o.name })),
|
|
5932
|
+
...options.agentID === undefined ? {} : { agentId: options.agentID, parentSessionId: deps.sessionId }
|
|
5933
|
+
};
|
|
5934
|
+
deps.emit.send(event);
|
|
5935
|
+
const raceOptions = {
|
|
5936
|
+
signal: composeSignals(deps.signal, options.signal)
|
|
5937
|
+
};
|
|
5938
|
+
if (deps.permissionTimeoutMs !== undefined) {
|
|
5939
|
+
raceOptions.timeoutMs = deps.permissionTimeoutMs;
|
|
5940
|
+
}
|
|
5941
|
+
if (deps.deferTimeoutMs !== undefined && deps.onDefer !== undefined) {
|
|
5942
|
+
raceOptions.deferTimeoutMs = deps.deferTimeoutMs;
|
|
5943
|
+
}
|
|
5944
|
+
let raced;
|
|
5945
|
+
try {
|
|
5946
|
+
raced = await awaitPermissionResponse(deps.server, "session/request_permission", payload, raceOptions);
|
|
5947
|
+
} catch (error) {
|
|
5948
|
+
if (error instanceof AcpTimeoutError) {
|
|
5949
|
+
return { behavior: "deny", message: "Permission RPC timed out" };
|
|
5950
|
+
}
|
|
5951
|
+
throw error;
|
|
5952
|
+
}
|
|
5953
|
+
if (raced === DEFERRED_SENTINEL) {
|
|
5954
|
+
if (deps.onDefer === undefined) {
|
|
5955
|
+
return { behavior: "deny", message: "Permission deferred (no handler)" };
|
|
5956
|
+
}
|
|
5957
|
+
return await deps.onDefer({ toolName, input, options });
|
|
5958
|
+
}
|
|
5959
|
+
return mapPermissionOutcome(raced, input, options.suggestions);
|
|
5960
|
+
};
|
|
5961
|
+
}
|
|
5962
|
+
function composeSignals(a, b) {
|
|
5963
|
+
if (a.aborted)
|
|
5964
|
+
return a;
|
|
5965
|
+
if (b.aborted)
|
|
5966
|
+
return b;
|
|
5967
|
+
const controller = new AbortController;
|
|
5968
|
+
const onAbort = () => controller.abort();
|
|
5969
|
+
a.addEventListener("abort", onAbort, { once: true });
|
|
5970
|
+
b.addEventListener("abort", onAbort, { once: true });
|
|
5971
|
+
return controller.signal;
|
|
5972
|
+
}
|
|
5973
|
+
function mapPermissionOutcome(response, input, suggestions) {
|
|
5974
|
+
const outcome = response.outcome;
|
|
5975
|
+
if (outcome.outcome === "cancelled") {
|
|
5976
|
+
throw new Error("Tool use aborted");
|
|
5977
|
+
}
|
|
5978
|
+
if (outcome.optionId === "allow") {
|
|
5979
|
+
return { behavior: "allow", updatedInput: input };
|
|
5980
|
+
}
|
|
5981
|
+
if (outcome.optionId === "allow_always") {
|
|
5982
|
+
return {
|
|
5983
|
+
behavior: "allow",
|
|
5984
|
+
updatedInput: input,
|
|
5985
|
+
updatedPermissions: suggestions !== undefined && suggestions.length > 0 ? suggestions : [
|
|
5986
|
+
{
|
|
5987
|
+
type: "addRules",
|
|
5988
|
+
rules: [{ toolName: "unknown" }],
|
|
5989
|
+
behavior: "allow",
|
|
5990
|
+
destination: "session"
|
|
5991
|
+
}
|
|
5992
|
+
]
|
|
5993
|
+
};
|
|
5994
|
+
}
|
|
5995
|
+
return { behavior: "deny", message: "User refused permission to run tool" };
|
|
5996
|
+
}
|
|
5997
|
+
var DEFAULT_OPTIONS;
|
|
5998
|
+
var init_permission_bridge = __esm(() => {
|
|
5999
|
+
init_errors();
|
|
6000
|
+
init_permission_rpc();
|
|
6001
|
+
DEFAULT_OPTIONS = [
|
|
6002
|
+
{ kind: "allow_always", name: "Always allow this tool", optionId: "allow_always" },
|
|
6003
|
+
{ kind: "allow_once", name: "Allow", optionId: "allow" },
|
|
6004
|
+
{ kind: "reject_once", name: "Reject", optionId: "reject" }
|
|
6005
|
+
];
|
|
6006
|
+
});
|
|
6007
|
+
|
|
5787
6008
|
// src/wire/policy.ts
|
|
5788
6009
|
function parseCanonicalPattern(input) {
|
|
5789
6010
|
if (input.length === 0) {
|
|
@@ -51522,7 +51743,7 @@ async function safeReply2(sdk, requestId, body) {
|
|
|
51522
51743
|
}
|
|
51523
51744
|
var CANONICAL_OPTIONS;
|
|
51524
51745
|
var init_permission_bridge2 = __esm(() => {
|
|
51525
|
-
|
|
51746
|
+
init_permission_rpc();
|
|
51526
51747
|
CANONICAL_OPTIONS = [
|
|
51527
51748
|
{ optionId: "allow", label: "Allow" },
|
|
51528
51749
|
{ optionId: "allow_always", label: "Always allow" },
|
|
@@ -52194,11 +52415,17 @@ function apiStopReason(reason) {
|
|
|
52194
52415
|
function turnFailed(hook) {
|
|
52195
52416
|
const error = text(hook.error) ?? "unknown";
|
|
52196
52417
|
const details = text(hook.error_details);
|
|
52418
|
+
return turnFailedWith(failureReason(error), details === undefined ? `StopFailure ${error}` : `StopFailure ${error}: ${details}`.slice(0, TITLE_MAX * 2));
|
|
52419
|
+
}
|
|
52420
|
+
function turnFailedWith(reason, detail) {
|
|
52197
52421
|
return draft("turn_failed", {
|
|
52198
|
-
reason
|
|
52199
|
-
detail
|
|
52422
|
+
reason,
|
|
52423
|
+
detail
|
|
52200
52424
|
});
|
|
52201
52425
|
}
|
|
52426
|
+
function failureReason(error) {
|
|
52427
|
+
return FAILURE_REASONS[error] ?? "api_error";
|
|
52428
|
+
}
|
|
52202
52429
|
function sessionEnded(reason) {
|
|
52203
52430
|
return draft("session_ended", { reason });
|
|
52204
52431
|
}
|
|
@@ -52365,11 +52592,30 @@ async function buildPaneSettings(options) {
|
|
|
52365
52592
|
}
|
|
52366
52593
|
]
|
|
52367
52594
|
]));
|
|
52368
|
-
|
|
52595
|
+
const settings = {
|
|
52369
52596
|
...base,
|
|
52370
52597
|
disableAllHooks: false,
|
|
52598
|
+
autoContinueAtUsageLimit: false,
|
|
52371
52599
|
hooks
|
|
52372
52600
|
};
|
|
52601
|
+
if (options.rules !== undefined) {
|
|
52602
|
+
settings.permissions = mergePermissions(base.permissions, options.rules);
|
|
52603
|
+
}
|
|
52604
|
+
return settings;
|
|
52605
|
+
}
|
|
52606
|
+
function mergePermissions(base, rules) {
|
|
52607
|
+
const permissions = isRecord2(base) ? base : {};
|
|
52608
|
+
const merge = (list) => {
|
|
52609
|
+
const own = permissions[list];
|
|
52610
|
+
const entries = Array.isArray(own) ? own.filter((entry) => typeof entry === "string") : [];
|
|
52611
|
+
return [...new Set([...entries, ...rules[list]])];
|
|
52612
|
+
};
|
|
52613
|
+
return {
|
|
52614
|
+
...permissions,
|
|
52615
|
+
allow: merge("allow"),
|
|
52616
|
+
deny: merge("deny"),
|
|
52617
|
+
ask: merge("ask")
|
|
52618
|
+
};
|
|
52373
52619
|
}
|
|
52374
52620
|
async function writePaneSettings(path, options) {
|
|
52375
52621
|
const settings = await buildPaneSettings(options);
|
|
@@ -52558,8 +52804,9 @@ var init_input = __esm(() => {
|
|
|
52558
52804
|
});
|
|
52559
52805
|
|
|
52560
52806
|
// src/live/pane.ts
|
|
52561
|
-
import {
|
|
52562
|
-
import {
|
|
52807
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
52808
|
+
import { mkdir as mkdir3, readFile as readFile3, realpath as realpath3, rename as rename4, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
52809
|
+
import { dirname as dirname4, join as join6, resolve as resolve5 } from "path";
|
|
52563
52810
|
function supportsPasteBufferS(versionOutput) {
|
|
52564
52811
|
const match = /^tmux (?:next-)?(\d+)\.(\d+)[a-z]?$/.exec(versionOutput.trim());
|
|
52565
52812
|
if (match === null) {
|
|
@@ -52585,7 +52832,7 @@ async function createPane(options) {
|
|
|
52585
52832
|
}
|
|
52586
52833
|
const env = paneEnv(options);
|
|
52587
52834
|
const cwd = await realpath3(options.cwd);
|
|
52588
|
-
await seedTrust(env.CLAUDE_CONFIG_DIR === undefined ? [env.HOME] : [env.HOME, env.CLAUDE_CONFIG_DIR], cwd);
|
|
52835
|
+
await seedTrust(env.CLAUDE_CONFIG_DIR === undefined ? [env.HOME] : [env.HOME, env.CLAUDE_CONFIG_DIR], await trustRoots(cwd), approvedKeySuffix(options.env?.ANTHROPIC_API_KEY));
|
|
52589
52836
|
const { PATH: path, TERM: term, ...sessionEnv } = env;
|
|
52590
52837
|
const created = await run(pane.socket, [
|
|
52591
52838
|
"start-server",
|
|
@@ -52674,11 +52921,27 @@ function paneEnv(options) {
|
|
|
52674
52921
|
...env,
|
|
52675
52922
|
HOME: options.home,
|
|
52676
52923
|
CI: "1",
|
|
52677
|
-
TERM: "xterm-256color"
|
|
52924
|
+
TERM: "xterm-256color",
|
|
52925
|
+
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1"
|
|
52678
52926
|
};
|
|
52679
52927
|
}
|
|
52680
52928
|
function launchArgv(options) {
|
|
52681
52929
|
const argv = [options.claudeBin ?? "claude", "--settings", options.settingsPath, "--setting-sources", ""];
|
|
52930
|
+
if (options.permissionMode !== undefined) {
|
|
52931
|
+
argv.push("--permission-mode", options.permissionMode);
|
|
52932
|
+
}
|
|
52933
|
+
if (options.mcpConfig !== undefined) {
|
|
52934
|
+
argv.push("--mcp-config", options.mcpConfig, "--strict-mcp-config");
|
|
52935
|
+
}
|
|
52936
|
+
if (options.systemPromptFile !== undefined) {
|
|
52937
|
+
argv.push("--append-system-prompt-file", options.systemPromptFile);
|
|
52938
|
+
}
|
|
52939
|
+
if (options.tools !== undefined) {
|
|
52940
|
+
argv.push("--tools", options.tools.join(","));
|
|
52941
|
+
}
|
|
52942
|
+
if (options.disallowedTools !== undefined && options.disallowedTools.length > 0) {
|
|
52943
|
+
argv.push("--disallowedTools", options.disallowedTools.join(","));
|
|
52944
|
+
}
|
|
52682
52945
|
if (options.model !== undefined) {
|
|
52683
52946
|
argv.push("--model", options.model);
|
|
52684
52947
|
}
|
|
@@ -52687,26 +52950,129 @@ function launchArgv(options) {
|
|
|
52687
52950
|
}
|
|
52688
52951
|
return argv;
|
|
52689
52952
|
}
|
|
52690
|
-
async function
|
|
52953
|
+
async function trustRoots(cwd) {
|
|
52954
|
+
const roots = new Set([cwd]);
|
|
52955
|
+
const repository = await findGitEntry(cwd);
|
|
52956
|
+
if (repository === null) {
|
|
52957
|
+
return [...roots];
|
|
52958
|
+
}
|
|
52959
|
+
roots.add(repository.root);
|
|
52960
|
+
if (repository.isFile) {
|
|
52961
|
+
const main = await worktreeMainRoot(repository.root);
|
|
52962
|
+
if (main !== null) {
|
|
52963
|
+
roots.add(main);
|
|
52964
|
+
}
|
|
52965
|
+
}
|
|
52966
|
+
return [...roots];
|
|
52967
|
+
}
|
|
52968
|
+
async function findGitEntry(start) {
|
|
52969
|
+
let dir = start;
|
|
52970
|
+
while (true) {
|
|
52971
|
+
const entry = await statIfPresent(join6(dir, ".git"));
|
|
52972
|
+
if (entry !== null) {
|
|
52973
|
+
return {
|
|
52974
|
+
root: dir,
|
|
52975
|
+
isFile: entry.isFile()
|
|
52976
|
+
};
|
|
52977
|
+
}
|
|
52978
|
+
const parent = dirname4(dir);
|
|
52979
|
+
if (parent === dir) {
|
|
52980
|
+
return null;
|
|
52981
|
+
}
|
|
52982
|
+
dir = parent;
|
|
52983
|
+
}
|
|
52984
|
+
}
|
|
52985
|
+
async function worktreeMainRoot(root) {
|
|
52986
|
+
const gitfile = await readTextIfPresent(join6(root, ".git"));
|
|
52987
|
+
const target = gitfile === null ? undefined : /^gitdir:\s*(.+?)\s*$/m.exec(gitfile)?.[1];
|
|
52988
|
+
if (target === undefined) {
|
|
52989
|
+
return null;
|
|
52990
|
+
}
|
|
52991
|
+
const gitdir = resolve5(root, target);
|
|
52992
|
+
const commondir = await readTextIfPresent(join6(gitdir, "commondir"));
|
|
52993
|
+
if (commondir === null) {
|
|
52994
|
+
return null;
|
|
52995
|
+
}
|
|
52996
|
+
return realpathIfPresent(dirname4(resolve5(gitdir, commondir.trim())));
|
|
52997
|
+
}
|
|
52998
|
+
async function statIfPresent(path) {
|
|
52999
|
+
try {
|
|
53000
|
+
return await stat2(path);
|
|
53001
|
+
} catch (error) {
|
|
53002
|
+
if (isAbsent(error)) {
|
|
53003
|
+
return null;
|
|
53004
|
+
}
|
|
53005
|
+
throw error;
|
|
53006
|
+
}
|
|
53007
|
+
}
|
|
53008
|
+
async function readTextIfPresent(path) {
|
|
53009
|
+
try {
|
|
53010
|
+
return await readFile3(path, "utf8");
|
|
53011
|
+
} catch (error) {
|
|
53012
|
+
if (isAbsent(error)) {
|
|
53013
|
+
return null;
|
|
53014
|
+
}
|
|
53015
|
+
throw error;
|
|
53016
|
+
}
|
|
53017
|
+
}
|
|
53018
|
+
async function realpathIfPresent(path) {
|
|
53019
|
+
try {
|
|
53020
|
+
return await realpath3(path);
|
|
53021
|
+
} catch (error) {
|
|
53022
|
+
if (isAbsent(error)) {
|
|
53023
|
+
return null;
|
|
53024
|
+
}
|
|
53025
|
+
throw error;
|
|
53026
|
+
}
|
|
53027
|
+
}
|
|
53028
|
+
function isAbsent(error) {
|
|
53029
|
+
return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
53030
|
+
}
|
|
53031
|
+
function approvedKeySuffix(apiKey) {
|
|
53032
|
+
return apiKey === undefined || apiKey === "" ? undefined : apiKey.slice(-20);
|
|
53033
|
+
}
|
|
53034
|
+
function approveKey(current, suffix) {
|
|
53035
|
+
const responses = isRecord3(current) ? current : {};
|
|
53036
|
+
const approved = stringList(responses.approved);
|
|
53037
|
+
return {
|
|
53038
|
+
...responses,
|
|
53039
|
+
approved: approved.includes(suffix) ? approved : [...approved, suffix],
|
|
53040
|
+
rejected: stringList(responses.rejected)
|
|
53041
|
+
};
|
|
53042
|
+
}
|
|
53043
|
+
function stringList(value) {
|
|
53044
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
53045
|
+
}
|
|
53046
|
+
function seedTrust(dirs, roots, keySuffix) {
|
|
53047
|
+
const seeded = seeding.then(() => writeTrust(dirs, roots, keySuffix));
|
|
53048
|
+
seeding = seeded.catch(() => {
|
|
53049
|
+
return;
|
|
53050
|
+
});
|
|
53051
|
+
return seeded;
|
|
53052
|
+
}
|
|
53053
|
+
async function writeTrust(dirs, roots, keySuffix) {
|
|
52691
53054
|
for (const dir of dirs) {
|
|
52692
53055
|
const path = join6(dir, ".claude.json");
|
|
52693
53056
|
const config = await readConfig(path);
|
|
52694
53057
|
const projects = isRecord3(config.projects) ? config.projects : {};
|
|
52695
|
-
const
|
|
52696
|
-
|
|
52697
|
-
|
|
52698
|
-
|
|
52699
|
-
|
|
52700
|
-
|
|
52701
|
-
|
|
53058
|
+
for (const root of roots) {
|
|
53059
|
+
const project = isRecord3(projects[root]) ? projects[root] : {};
|
|
53060
|
+
projects[root] = {
|
|
53061
|
+
allowedTools: [],
|
|
53062
|
+
...project,
|
|
53063
|
+
hasTrustDialogAccepted: true,
|
|
53064
|
+
hasCompletedProjectOnboarding: true
|
|
53065
|
+
};
|
|
53066
|
+
}
|
|
52702
53067
|
const seeded = {
|
|
52703
53068
|
theme: "dark",
|
|
52704
53069
|
...config,
|
|
52705
53070
|
hasCompletedOnboarding: true,
|
|
52706
|
-
projects
|
|
53071
|
+
projects,
|
|
53072
|
+
...keySuffix === undefined ? {} : { customApiKeyResponses: approveKey(config.customApiKeyResponses, keySuffix) }
|
|
52707
53073
|
};
|
|
52708
53074
|
await mkdir3(dir, { recursive: true });
|
|
52709
|
-
const staging = `${path}.${
|
|
53075
|
+
const staging = `${path}.${randomUUID5()}.tmp`;
|
|
52710
53076
|
await writeFile2(staging, JSON.stringify(seeded, null, 2), { mode: 384 });
|
|
52711
53077
|
await rename4(staging, path);
|
|
52712
53078
|
}
|
|
@@ -52791,7 +53157,7 @@ async function failOnPane(pane, stderr) {
|
|
|
52791
53157
|
}
|
|
52792
53158
|
throw new PaneError("tmux_failed", stderr);
|
|
52793
53159
|
}
|
|
52794
|
-
var LIVE_SOCKET = "kodizm-live", PASTE_ENTER_DELAY_MS = 328, SUBMIT_WINDOWS_MS, SESSION_START_TIMEOUT_MS = 1e4, CANCEL_CLEAR_DELAY_MS = 414, PANE_ID, RESUME_ID, DROPPED_ENV, PASTE_S_SINCE, PaneError, bufferSequence = 0, pasteSFlags;
|
|
53160
|
+
var LIVE_SOCKET = "kodizm-live", PASTE_ENTER_DELAY_MS = 328, SUBMIT_WINDOWS_MS, SESSION_START_TIMEOUT_MS = 1e4, CANCEL_CLEAR_DELAY_MS = 414, PANE_ID, RESUME_ID, DROPPED_ENV, PASTE_S_SINCE, PaneError, bufferSequence = 0, seeding, pasteSFlags;
|
|
52795
53161
|
var init_pane = __esm(() => {
|
|
52796
53162
|
init_input();
|
|
52797
53163
|
SUBMIT_WINDOWS_MS = [328, 656, 1312, 2624];
|
|
@@ -52807,11 +53173,225 @@ var init_pane = __esm(() => {
|
|
|
52807
53173
|
this.reason = reason;
|
|
52808
53174
|
}
|
|
52809
53175
|
};
|
|
53176
|
+
seeding = Promise.resolve();
|
|
52810
53177
|
pasteSFlags = new Map;
|
|
52811
53178
|
});
|
|
52812
53179
|
|
|
53180
|
+
// src/live/policy.ts
|
|
53181
|
+
function parseToolPolicy(text) {
|
|
53182
|
+
const policy = parseJson2(text);
|
|
53183
|
+
if (!isRecord4(policy) || !Object.keys(policy).every((key) => POLICY_KEYS.includes(key))) {
|
|
53184
|
+
return null;
|
|
53185
|
+
}
|
|
53186
|
+
const rules = {
|
|
53187
|
+
allow: [],
|
|
53188
|
+
deny: [],
|
|
53189
|
+
ask: []
|
|
53190
|
+
};
|
|
53191
|
+
for (const list of RULE_LISTS) {
|
|
53192
|
+
const entries = policy[list] === undefined ? [] : policy[list];
|
|
53193
|
+
if (!Array.isArray(entries) || !entries.every((entry) => typeof entry === "string")) {
|
|
53194
|
+
return null;
|
|
53195
|
+
}
|
|
53196
|
+
for (const entry of entries) {
|
|
53197
|
+
const rule = translatePattern(entry, list);
|
|
53198
|
+
if (rule === null) {
|
|
53199
|
+
return null;
|
|
53200
|
+
}
|
|
53201
|
+
if (!rules[list].includes(rule)) {
|
|
53202
|
+
rules[list].push(rule);
|
|
53203
|
+
}
|
|
53204
|
+
}
|
|
53205
|
+
}
|
|
53206
|
+
return rules;
|
|
53207
|
+
}
|
|
53208
|
+
function translatePattern(pattern, list) {
|
|
53209
|
+
let parsed;
|
|
53210
|
+
try {
|
|
53211
|
+
parsed = parseCanonicalPattern(pattern);
|
|
53212
|
+
} catch (error) {
|
|
53213
|
+
if (error instanceof InvalidPatternError) {
|
|
53214
|
+
return null;
|
|
53215
|
+
}
|
|
53216
|
+
throw error;
|
|
53217
|
+
}
|
|
53218
|
+
if (parsed.mcpPath !== undefined) {
|
|
53219
|
+
return mcpRule(parsed.mcpPath);
|
|
53220
|
+
}
|
|
53221
|
+
const { toolName, argPattern } = parsed;
|
|
53222
|
+
if (!TOOL_NAME.test(toolName)) {
|
|
53223
|
+
return null;
|
|
53224
|
+
}
|
|
53225
|
+
if (argPattern === undefined || argPattern === "*") {
|
|
53226
|
+
return toolName;
|
|
53227
|
+
}
|
|
53228
|
+
const fileTool = FILE_TOOLS.get(toolName);
|
|
53229
|
+
if (fileTool !== undefined && fileTool !== toolName && list === "allow") {
|
|
53230
|
+
return null;
|
|
53231
|
+
}
|
|
53232
|
+
const tool = fileTool ?? toolName;
|
|
53233
|
+
const content = ruleContent(tool, argPattern, list);
|
|
53234
|
+
return isEnforceableContent(tool, content) ? `${tool}(${escapeRuleContent(content)})` : null;
|
|
53235
|
+
}
|
|
53236
|
+
function mcpRule(path) {
|
|
53237
|
+
const wildcardMisplaced = path.some((segment, index) => segment.includes("*") && !(index === 1 && segment === "*"));
|
|
53238
|
+
if (path.length > 2 || wildcardMisplaced || path.some((segment) => /[()]/.test(segment))) {
|
|
53239
|
+
return null;
|
|
53240
|
+
}
|
|
53241
|
+
return `mcp__${path.join("__")}`;
|
|
53242
|
+
}
|
|
53243
|
+
function ruleContent(tool, argPattern, list) {
|
|
53244
|
+
if (tool === "Read" || tool === "Edit") {
|
|
53245
|
+
return anchorPath(argPattern);
|
|
53246
|
+
}
|
|
53247
|
+
return tool === "Bash" ? bashArg(argPattern, list) : argPattern;
|
|
53248
|
+
}
|
|
53249
|
+
function anchorPath(path) {
|
|
53250
|
+
return path.startsWith("/") && !path.startsWith("//") ? `/${path}` : path;
|
|
53251
|
+
}
|
|
53252
|
+
function bashArg(argPattern, list) {
|
|
53253
|
+
const prefix = argPattern.slice(0, -1);
|
|
53254
|
+
if (list !== "allow" || !argPattern.endsWith("*") || prefix.includes("*") || /[:\s]$/.test(prefix)) {
|
|
53255
|
+
return argPattern;
|
|
53256
|
+
}
|
|
53257
|
+
return `${prefix}:*`;
|
|
53258
|
+
}
|
|
53259
|
+
function escapeRuleContent(content) {
|
|
53260
|
+
return content.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
|
|
53261
|
+
}
|
|
53262
|
+
function isEnforceableContent(tool, content) {
|
|
53263
|
+
if (tool === "Bash") {
|
|
53264
|
+
return (!content.includes(":*") || content.endsWith(":*")) && content !== ":*";
|
|
53265
|
+
}
|
|
53266
|
+
if (tool === "Read" || tool === "Edit") {
|
|
53267
|
+
return !content.includes(":*") && (!content.includes("*") || /^\*|\*$|\*\*|\/\*|\*\.|\*\)/.test(content));
|
|
53268
|
+
}
|
|
53269
|
+
return true;
|
|
53270
|
+
}
|
|
53271
|
+
function parseJson2(text) {
|
|
53272
|
+
try {
|
|
53273
|
+
return JSON.parse(text);
|
|
53274
|
+
} catch (error) {
|
|
53275
|
+
if (error instanceof SyntaxError) {
|
|
53276
|
+
return;
|
|
53277
|
+
}
|
|
53278
|
+
throw error;
|
|
53279
|
+
}
|
|
53280
|
+
}
|
|
53281
|
+
function isRecord4(value) {
|
|
53282
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
53283
|
+
}
|
|
53284
|
+
var RULE_LISTS, POLICY_KEYS, FILE_TOOLS, TOOL_NAME;
|
|
53285
|
+
var init_policy4 = __esm(() => {
|
|
53286
|
+
init_policy();
|
|
53287
|
+
RULE_LISTS = ["allow", "deny", "ask"];
|
|
53288
|
+
POLICY_KEYS = [...RULE_LISTS, "defaultMode"];
|
|
53289
|
+
FILE_TOOLS = new Map([
|
|
53290
|
+
["Read", "Read"],
|
|
53291
|
+
["Edit", "Edit"],
|
|
53292
|
+
["Write", "Edit"],
|
|
53293
|
+
["MultiEdit", "Edit"],
|
|
53294
|
+
["NotebookEdit", "Edit"],
|
|
53295
|
+
["Glob", "Read"],
|
|
53296
|
+
["Grep", "Read"]
|
|
53297
|
+
]);
|
|
53298
|
+
TOOL_NAME = /^[A-Z][A-Za-z0-9_]*$/;
|
|
53299
|
+
});
|
|
53300
|
+
|
|
53301
|
+
// src/live/start-flags.ts
|
|
53302
|
+
function parsePaneFeatures(content, permissionMode) {
|
|
53303
|
+
let json;
|
|
53304
|
+
try {
|
|
53305
|
+
json = JSON.parse(content);
|
|
53306
|
+
} catch {
|
|
53307
|
+
return null;
|
|
53308
|
+
}
|
|
53309
|
+
const parsed = FeaturesSchema.safeParse(json);
|
|
53310
|
+
if (!parsed.success || parsed.data.simple === true) {
|
|
53311
|
+
return null;
|
|
53312
|
+
}
|
|
53313
|
+
const names = parsed.data.tools;
|
|
53314
|
+
if (names !== undefined && (names.length === 0 || !names.every((name) => TOOL_NAME2.test(name)))) {
|
|
53315
|
+
return null;
|
|
53316
|
+
}
|
|
53317
|
+
const translated = translateFeaturesToClaude(parsed.data, permissionMode);
|
|
53318
|
+
return { tools: translated.tools, disallowedTools: translated.disallowedTools, env: translated.env };
|
|
53319
|
+
}
|
|
53320
|
+
function parseStartExtras(args) {
|
|
53321
|
+
for (const key of Object.keys(args)) {
|
|
53322
|
+
if (!START_FLAGS.has(key)) {
|
|
53323
|
+
return null;
|
|
53324
|
+
}
|
|
53325
|
+
}
|
|
53326
|
+
const permissionMode = parsePermissionMode(args["permission-mode"]);
|
|
53327
|
+
const oneShot = parseOneShot(args["one-shot"]);
|
|
53328
|
+
const deadlineSeconds = parseSeconds(args["deadline-seconds"], 1);
|
|
53329
|
+
const retainFailedSeconds = parseSeconds(args["retain-failed-seconds"], 0);
|
|
53330
|
+
if (permissionMode === null || oneShot === null || deadlineSeconds === null || retainFailedSeconds === null) {
|
|
53331
|
+
return null;
|
|
53332
|
+
}
|
|
53333
|
+
return {
|
|
53334
|
+
permissionMode,
|
|
53335
|
+
oneShot,
|
|
53336
|
+
deadlineSeconds,
|
|
53337
|
+
retainFailedSeconds: retainFailedSeconds ?? 0
|
|
53338
|
+
};
|
|
53339
|
+
}
|
|
53340
|
+
function parsePermissionMode(value) {
|
|
53341
|
+
if (value === undefined) {
|
|
53342
|
+
return;
|
|
53343
|
+
}
|
|
53344
|
+
return value === "default" || value === "dontAsk" ? value : null;
|
|
53345
|
+
}
|
|
53346
|
+
function parseOneShot(value) {
|
|
53347
|
+
if (value === undefined || value === "0") {
|
|
53348
|
+
return false;
|
|
53349
|
+
}
|
|
53350
|
+
return value === "1" ? true : null;
|
|
53351
|
+
}
|
|
53352
|
+
function parseSeconds(value, min) {
|
|
53353
|
+
if (value === undefined) {
|
|
53354
|
+
return;
|
|
53355
|
+
}
|
|
53356
|
+
if (!DIGITS.test(value)) {
|
|
53357
|
+
return null;
|
|
53358
|
+
}
|
|
53359
|
+
const seconds = Number(value);
|
|
53360
|
+
return seconds >= min && seconds <= MAX_SECONDS ? seconds : null;
|
|
53361
|
+
}
|
|
53362
|
+
var START_FLAGS, START_FILE_FLAGS, TOOL_NAME2, MAX_SECONDS = 2147483, DIGITS;
|
|
53363
|
+
var init_start_flags = __esm(() => {
|
|
53364
|
+
init_features();
|
|
53365
|
+
init_schemas3();
|
|
53366
|
+
START_FLAGS = new Set([
|
|
53367
|
+
"pane",
|
|
53368
|
+
"cwd",
|
|
53369
|
+
"settings-base",
|
|
53370
|
+
"env-file",
|
|
53371
|
+
"model",
|
|
53372
|
+
"resume",
|
|
53373
|
+
"mcp-config",
|
|
53374
|
+
"tool-policy",
|
|
53375
|
+
"system-prompt-file",
|
|
53376
|
+
"features",
|
|
53377
|
+
"permission-mode",
|
|
53378
|
+
"one-shot",
|
|
53379
|
+
"deadline-seconds",
|
|
53380
|
+
"retain-failed-seconds"
|
|
53381
|
+
]);
|
|
53382
|
+
START_FILE_FLAGS = [
|
|
53383
|
+
"env-file",
|
|
53384
|
+
"tool-policy",
|
|
53385
|
+
"system-prompt-file",
|
|
53386
|
+
"mcp-config",
|
|
53387
|
+
"features"
|
|
53388
|
+
];
|
|
53389
|
+
TOOL_NAME2 = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
|
|
53390
|
+
DIGITS = /^[0-9]+$/;
|
|
53391
|
+
});
|
|
53392
|
+
|
|
52813
53393
|
// src/live/transcript.ts
|
|
52814
|
-
import { stat as
|
|
53394
|
+
import { stat as stat3 } from "fs/promises";
|
|
52815
53395
|
|
|
52816
53396
|
class TranscriptTail {
|
|
52817
53397
|
path;
|
|
@@ -52875,7 +53455,7 @@ function classifyInterrupt(record) {
|
|
|
52875
53455
|
return null;
|
|
52876
53456
|
}
|
|
52877
53457
|
const [block] = content;
|
|
52878
|
-
if (!
|
|
53458
|
+
if (!isRecord5(block) || block.type !== "text") {
|
|
52879
53459
|
return null;
|
|
52880
53460
|
}
|
|
52881
53461
|
if (block.text === INTERRUPT_MARKER) {
|
|
@@ -52886,6 +53466,29 @@ function classifyInterrupt(record) {
|
|
|
52886
53466
|
}
|
|
52887
53467
|
return null;
|
|
52888
53468
|
}
|
|
53469
|
+
function classifyApiError(record) {
|
|
53470
|
+
if (record.type !== "assistant" || !isMainChain(record) || record.isApiErrorMessage !== true) {
|
|
53471
|
+
return null;
|
|
53472
|
+
}
|
|
53473
|
+
const error = typeof record.error === "string" ? record.error : "unknown";
|
|
53474
|
+
const status = typeof record.apiErrorStatus === "number" ? record.apiErrorStatus : null;
|
|
53475
|
+
const message = assistantBlocks(record).filter((block) => block.kind === "text").map((block) => block.text).join(`
|
|
53476
|
+
`);
|
|
53477
|
+
return {
|
|
53478
|
+
reason: apiErrorReason(error, status, message),
|
|
53479
|
+
detail: `API error ${status ?? error}: ${message.slice(0, API_ERROR_DETAIL_MAX)}`
|
|
53480
|
+
};
|
|
53481
|
+
}
|
|
53482
|
+
function apiErrorReason(error, status, message) {
|
|
53483
|
+
if (error === "rate_limit" && USAGE_LIMIT_TEXT.test(message) && LIMIT_RESETS.test(message) && !NOT_A_USAGE_LIMIT.test(message)) {
|
|
53484
|
+
return "usage_limit";
|
|
53485
|
+
}
|
|
53486
|
+
const reason = failureReason(error);
|
|
53487
|
+
if (reason !== "api_error" || status === null) {
|
|
53488
|
+
return reason;
|
|
53489
|
+
}
|
|
53490
|
+
return STATUS_REASONS[status] ?? reason;
|
|
53491
|
+
}
|
|
52889
53492
|
|
|
52890
53493
|
class UsageLedger {
|
|
52891
53494
|
counted = new Map;
|
|
@@ -52894,7 +53497,7 @@ class UsageLedger {
|
|
|
52894
53497
|
if (record.type !== "assistant" || !isMainChain(record) || message === null) {
|
|
52895
53498
|
return null;
|
|
52896
53499
|
}
|
|
52897
|
-
if (typeof message.id !== "string" || !
|
|
53500
|
+
if (typeof message.id !== "string" || !isRecord5(message.usage)) {
|
|
52898
53501
|
return null;
|
|
52899
53502
|
}
|
|
52900
53503
|
const seen = this.counted.get(message.id) ?? emptyCounts();
|
|
@@ -52921,7 +53524,7 @@ function assistantBlocks(record) {
|
|
|
52921
53524
|
}
|
|
52922
53525
|
const blocks = [];
|
|
52923
53526
|
for (const block of content) {
|
|
52924
|
-
if (!
|
|
53527
|
+
if (!isRecord5(block)) {
|
|
52925
53528
|
continue;
|
|
52926
53529
|
}
|
|
52927
53530
|
if (block.type === "text" && typeof block.text === "string" && block.text !== "") {
|
|
@@ -52941,12 +53544,12 @@ function isMainChain(record) {
|
|
|
52941
53544
|
return record.isSidechain !== true;
|
|
52942
53545
|
}
|
|
52943
53546
|
function messageOf(record) {
|
|
52944
|
-
return
|
|
53547
|
+
return isRecord5(record.message) ? record.message : null;
|
|
52945
53548
|
}
|
|
52946
53549
|
function parseLine(line) {
|
|
52947
53550
|
try {
|
|
52948
53551
|
const value = JSON.parse(line);
|
|
52949
|
-
return
|
|
53552
|
+
return isRecord5(value) ? value : null;
|
|
52950
53553
|
} catch (error) {
|
|
52951
53554
|
if (error instanceof SyntaxError) {
|
|
52952
53555
|
return null;
|
|
@@ -52956,7 +53559,7 @@ function parseLine(line) {
|
|
|
52956
53559
|
}
|
|
52957
53560
|
async function fileSize(path) {
|
|
52958
53561
|
try {
|
|
52959
|
-
return (await
|
|
53562
|
+
return (await stat3(path)).size;
|
|
52960
53563
|
} catch (error) {
|
|
52961
53564
|
if (error.code === "ENOENT") {
|
|
52962
53565
|
return null;
|
|
@@ -52984,17 +53587,27 @@ function emptyCounts() {
|
|
|
52984
53587
|
cacheCreationTokens: 0
|
|
52985
53588
|
};
|
|
52986
53589
|
}
|
|
52987
|
-
function
|
|
53590
|
+
function isRecord5(value) {
|
|
52988
53591
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
52989
53592
|
}
|
|
52990
|
-
var INTERRUPT_MARKER = "[Request interrupted by user]", INTERRUPT_TOOL_MARKER = "[Request interrupted by user for tool use]", NEWLINE = 10;
|
|
52991
|
-
var init_transcript = () => {
|
|
53593
|
+
var INTERRUPT_MARKER = "[Request interrupted by user]", INTERRUPT_TOOL_MARKER = "[Request interrupted by user for tool use]", NEWLINE = 10, USAGE_LIMIT_TEXT, LIMIT_RESETS, NOT_A_USAGE_LIMIT, STATUS_REASONS, API_ERROR_DETAIL_MAX = 200;
|
|
53594
|
+
var init_transcript = __esm(() => {
|
|
53595
|
+
init_events2();
|
|
53596
|
+
USAGE_LIMIT_TEXT = /\b(usage|session|weekly) limit\b|\bhit your\b[^.\n]*\blimit\b|\bout of extra usage\b/i;
|
|
53597
|
+
LIMIT_RESETS = /\bresets?\b/i;
|
|
53598
|
+
NOT_A_USAGE_LIMIT = /\bnot your (usage )?limit\b/i;
|
|
53599
|
+
STATUS_REASONS = {
|
|
53600
|
+
429: "rate_limit",
|
|
53601
|
+
401: "auth_error"
|
|
53602
|
+
};
|
|
53603
|
+
});
|
|
52992
53604
|
|
|
52993
53605
|
// src/live/daemon.ts
|
|
52994
|
-
import { randomUUID as
|
|
52995
|
-
import {
|
|
53606
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
53607
|
+
import { renameSync as renameSync2, rmSync as rmSync2, writeFileSync } from "fs";
|
|
53608
|
+
import { chmod, lstat as lstat2, mkdir as mkdir4, readFile as readFile4, realpath as realpath4, unlink as unlink2 } from "fs/promises";
|
|
52996
53609
|
import { createServer } from "net";
|
|
52997
|
-
import { dirname as
|
|
53610
|
+
import { dirname as dirname5, isAbsolute as isAbsolute4, join as join7 } from "path";
|
|
52998
53611
|
function exitCodeFor(code) {
|
|
52999
53612
|
return EXIT_CODES[code];
|
|
53000
53613
|
}
|
|
@@ -53157,6 +53770,9 @@ class LivePane {
|
|
|
53157
53770
|
gen;
|
|
53158
53771
|
ingestUrl;
|
|
53159
53772
|
daemon;
|
|
53773
|
+
extras;
|
|
53774
|
+
mcpConfig;
|
|
53775
|
+
paneDir;
|
|
53160
53776
|
started = new Latch;
|
|
53161
53777
|
submitted = new Latch;
|
|
53162
53778
|
turnClosed = new Latch;
|
|
@@ -53179,17 +53795,29 @@ class LivePane {
|
|
|
53179
53795
|
skippedLines = 0;
|
|
53180
53796
|
lastAssistantText;
|
|
53181
53797
|
lastStopReason = null;
|
|
53798
|
+
apiError;
|
|
53182
53799
|
settled = false;
|
|
53183
53800
|
lastActivityAt = Date.now();
|
|
53184
53801
|
sessionEndReason;
|
|
53185
53802
|
timers = [];
|
|
53803
|
+
heartbeatTimer;
|
|
53804
|
+
deadlineTimer;
|
|
53805
|
+
retentionTimer;
|
|
53806
|
+
interruptTimer;
|
|
53807
|
+
interrupt;
|
|
53808
|
+
isRetaining = false;
|
|
53809
|
+
ending = false;
|
|
53810
|
+
teardown;
|
|
53186
53811
|
monitoring = false;
|
|
53187
53812
|
closed = false;
|
|
53188
|
-
constructor(pane, gen, ingestUrl, token, authValues, daemon) {
|
|
53813
|
+
constructor(pane, gen, ingestUrl, token, authValues, daemon, extras, mcpConfig, paneDir) {
|
|
53189
53814
|
this.pane = pane;
|
|
53190
53815
|
this.gen = gen;
|
|
53191
53816
|
this.ingestUrl = ingestUrl;
|
|
53192
53817
|
this.daemon = daemon;
|
|
53818
|
+
this.extras = extras;
|
|
53819
|
+
this.mcpConfig = mcpConfig;
|
|
53820
|
+
this.paneDir = paneDir;
|
|
53193
53821
|
this.token = token;
|
|
53194
53822
|
this.secrets = [token, ...authValues.filter((value) => value.length >= MIN_SECRET_LENGTH)];
|
|
53195
53823
|
this.queue = new EventQueue({
|
|
@@ -53198,7 +53826,11 @@ class LivePane {
|
|
|
53198
53826
|
bearer: () => this.token,
|
|
53199
53827
|
secrets: () => this.secrets,
|
|
53200
53828
|
timings: () => daemon.timings,
|
|
53201
|
-
onStaleGen: () =>
|
|
53829
|
+
onStaleGen: () => {
|
|
53830
|
+
if (!this.isRetaining) {
|
|
53831
|
+
this.abandon();
|
|
53832
|
+
}
|
|
53833
|
+
},
|
|
53202
53834
|
logger: daemon.logger
|
|
53203
53835
|
});
|
|
53204
53836
|
}
|
|
@@ -53208,13 +53840,16 @@ class LivePane {
|
|
|
53208
53840
|
get busy() {
|
|
53209
53841
|
return this.turn !== undefined || this.compacting;
|
|
53210
53842
|
}
|
|
53843
|
+
get refusesInput() {
|
|
53844
|
+
return this.ending || this.interrupt !== undefined;
|
|
53845
|
+
}
|
|
53211
53846
|
begin() {
|
|
53212
53847
|
const timings = this.daemon.timings;
|
|
53213
53848
|
this.timers = [
|
|
53214
53849
|
setInterval(() => void this.guard("transcript", () => this.readTranscript()), timings.transcriptPollMs),
|
|
53215
|
-
setInterval(() => void this.guard("monitor", () => this.monitor()), timings.monitorMs)
|
|
53216
|
-
setInterval(() => void this.guard("heartbeat", () => this.beat()), timings.heartbeatMs)
|
|
53850
|
+
setInterval(() => void this.guard("monitor", () => this.monitor()), timings.monitorMs)
|
|
53217
53851
|
];
|
|
53852
|
+
this.heartbeatTimer = setInterval(() => void this.guard("heartbeat", () => this.beat()), timings.heartbeatMs);
|
|
53218
53853
|
}
|
|
53219
53854
|
rotate(token) {
|
|
53220
53855
|
this.token = token;
|
|
@@ -53280,24 +53915,28 @@ class LivePane {
|
|
|
53280
53915
|
}
|
|
53281
53916
|
this.closeOpenTools();
|
|
53282
53917
|
this.clearDialog();
|
|
53918
|
+
const message = text(draft.payload.lastAssistantMessage);
|
|
53919
|
+
if (draft.type === "turn_completed" && message !== undefined) {
|
|
53920
|
+
this.persistLastAssistantMessage(message);
|
|
53921
|
+
}
|
|
53283
53922
|
this.queue.push(draft);
|
|
53284
53923
|
this.turn = undefined;
|
|
53924
|
+
clearTimeout(this.deadlineTimer);
|
|
53925
|
+
this.deadlineTimer = undefined;
|
|
53926
|
+
this.lastActivityAt = Date.now();
|
|
53285
53927
|
this.lastCloseCause = cause;
|
|
53286
53928
|
this.turnClosed.fire();
|
|
53929
|
+
this.afterTurn(draft.type);
|
|
53287
53930
|
}
|
|
53288
53931
|
clearDialog() {
|
|
53289
53932
|
this.dialog?.abort.abort();
|
|
53290
53933
|
this.dialog = undefined;
|
|
53291
53934
|
}
|
|
53292
53935
|
interruptedTurn() {
|
|
53293
|
-
return turnCompleted("interrupted", this.lastAssistantText);
|
|
53936
|
+
return turnCompleted("interrupted", this.lastAssistantText ?? "");
|
|
53294
53937
|
}
|
|
53295
|
-
|
|
53296
|
-
this.
|
|
53297
|
-
await killPane(this.pane);
|
|
53298
|
-
this.queue.push(sessionEnded("stopped"));
|
|
53299
|
-
await this.queue.drain(STOP_DRAIN_MS);
|
|
53300
|
-
this.queue.stop();
|
|
53938
|
+
stop(reason = "stopped") {
|
|
53939
|
+
return this.tearDown(() => this.finish(reason, () => killPane(this.pane)));
|
|
53301
53940
|
}
|
|
53302
53941
|
async dispose() {
|
|
53303
53942
|
this.close();
|
|
@@ -53309,7 +53948,130 @@ class LivePane {
|
|
|
53309
53948
|
for (const timer of this.timers.splice(0)) {
|
|
53310
53949
|
clearInterval(timer);
|
|
53311
53950
|
}
|
|
53951
|
+
clearInterval(this.heartbeatTimer);
|
|
53952
|
+
clearTimeout(this.deadlineTimer);
|
|
53953
|
+
clearTimeout(this.retentionTimer);
|
|
53954
|
+
clearTimeout(this.interruptTimer);
|
|
53312
53955
|
this.clearDialog();
|
|
53956
|
+
this.removeMcpConfig();
|
|
53957
|
+
}
|
|
53958
|
+
tearDown(run) {
|
|
53959
|
+
this.teardown ??= run();
|
|
53960
|
+
return this.teardown;
|
|
53961
|
+
}
|
|
53962
|
+
async finish(reason, kill) {
|
|
53963
|
+
try {
|
|
53964
|
+
this.close();
|
|
53965
|
+
await kill();
|
|
53966
|
+
this.queue.push(sessionEnded(reason));
|
|
53967
|
+
await this.queue.drain(STOP_DRAIN_MS);
|
|
53968
|
+
this.queue.stop();
|
|
53969
|
+
} finally {
|
|
53970
|
+
this.daemon.remove(this);
|
|
53971
|
+
}
|
|
53972
|
+
}
|
|
53973
|
+
afterTurn(type) {
|
|
53974
|
+
if (this.closed || this.ending) {
|
|
53975
|
+
return;
|
|
53976
|
+
}
|
|
53977
|
+
const { oneShot, retainFailedSeconds } = this.extras;
|
|
53978
|
+
if (type === "turn_completed" && oneShot) {
|
|
53979
|
+
this.ending = true;
|
|
53980
|
+
this.guard("end", () => this.endAfterDelivery(0));
|
|
53981
|
+
return;
|
|
53982
|
+
}
|
|
53983
|
+
if (type !== "turn_failed" || !oneShot && retainFailedSeconds === 0) {
|
|
53984
|
+
return;
|
|
53985
|
+
}
|
|
53986
|
+
this.ending = true;
|
|
53987
|
+
if (retainFailedSeconds > 0) {
|
|
53988
|
+
this.isRetaining = true;
|
|
53989
|
+
clearInterval(this.heartbeatTimer);
|
|
53990
|
+
}
|
|
53991
|
+
this.guard("end", () => this.endAfterDelivery(retainFailedSeconds * this.daemon.timings.secondMs));
|
|
53992
|
+
}
|
|
53993
|
+
async endAfterDelivery(delayMs) {
|
|
53994
|
+
await this.queue.drain(this.daemon.timings.deliveryDrainMs);
|
|
53995
|
+
if (this.closed) {
|
|
53996
|
+
return;
|
|
53997
|
+
}
|
|
53998
|
+
if (delayMs > 0) {
|
|
53999
|
+
await new Promise((resolve) => {
|
|
54000
|
+
this.retentionTimer = setTimeout(resolve, delayMs);
|
|
54001
|
+
});
|
|
54002
|
+
if (this.closed) {
|
|
54003
|
+
return;
|
|
54004
|
+
}
|
|
54005
|
+
}
|
|
54006
|
+
await this.end("stopped");
|
|
54007
|
+
}
|
|
54008
|
+
end(reason) {
|
|
54009
|
+
return this.tearDown(() => this.finish(reason, () => this.killLogged()));
|
|
54010
|
+
}
|
|
54011
|
+
async onDeadline(promptId, seconds) {
|
|
54012
|
+
if (this.closed || this.turn?.promptId !== promptId) {
|
|
54013
|
+
return;
|
|
54014
|
+
}
|
|
54015
|
+
this.closeTurn(turnFailedWith("deadline", `The turn ran longer than ${seconds} seconds.`), "deadline");
|
|
54016
|
+
const interrupt = {
|
|
54017
|
+
sent: false,
|
|
54018
|
+
marked: false
|
|
54019
|
+
};
|
|
54020
|
+
this.interrupt = interrupt;
|
|
54021
|
+
this.interruptTimer = setTimeout(() => this.endInterrupt(), this.daemon.timings.cancelTimeoutMs);
|
|
54022
|
+
try {
|
|
54023
|
+
await cancelPane(this.pane);
|
|
54024
|
+
interrupt.sent = true;
|
|
54025
|
+
this.settleInterrupt();
|
|
54026
|
+
} catch (error) {
|
|
54027
|
+
if (!(error instanceof PaneError)) {
|
|
54028
|
+
throw error;
|
|
54029
|
+
}
|
|
54030
|
+
this.daemon.logger.warn("tmuxd could not interrupt a turn past its deadline", {
|
|
54031
|
+
pane: this.id,
|
|
54032
|
+
reason: error.reason
|
|
54033
|
+
});
|
|
54034
|
+
}
|
|
54035
|
+
}
|
|
54036
|
+
settleInterrupt() {
|
|
54037
|
+
if (this.interrupt?.sent === true && this.interrupt.marked) {
|
|
54038
|
+
this.endInterrupt();
|
|
54039
|
+
}
|
|
54040
|
+
}
|
|
54041
|
+
endInterrupt() {
|
|
54042
|
+
clearTimeout(this.interruptTimer);
|
|
54043
|
+
this.interruptTimer = undefined;
|
|
54044
|
+
this.interrupt = undefined;
|
|
54045
|
+
}
|
|
54046
|
+
removeMcpConfig() {
|
|
54047
|
+
if (this.mcpConfig === undefined) {
|
|
54048
|
+
return;
|
|
54049
|
+
}
|
|
54050
|
+
try {
|
|
54051
|
+
rmSync2(this.mcpConfig, { force: true });
|
|
54052
|
+
} catch (error) {
|
|
54053
|
+
if (!isErrno(error)) {
|
|
54054
|
+
throw error;
|
|
54055
|
+
}
|
|
54056
|
+
this.daemon.logger.error("tmuxd could not remove the mcp-config", { pane: this.id, code: error.code });
|
|
54057
|
+
}
|
|
54058
|
+
}
|
|
54059
|
+
persistLastAssistantMessage(message) {
|
|
54060
|
+
const path = join7(this.paneDir, LAST_MESSAGE_FILE);
|
|
54061
|
+
const temp = `${path}.${randomUUID6()}.tmp`;
|
|
54062
|
+
try {
|
|
54063
|
+
writeFileSync(temp, redactDeep(message, this.secrets), { mode: 384, flag: "wx" });
|
|
54064
|
+
renameSync2(temp, path);
|
|
54065
|
+
} catch (error) {
|
|
54066
|
+
if (!isErrno(error)) {
|
|
54067
|
+
throw error;
|
|
54068
|
+
}
|
|
54069
|
+
rmSync2(temp, { force: true });
|
|
54070
|
+
this.daemon.logger.error("tmuxd could not persist the last assistant message", {
|
|
54071
|
+
pane: this.id,
|
|
54072
|
+
code: error.code
|
|
54073
|
+
});
|
|
54074
|
+
}
|
|
53313
54075
|
}
|
|
53314
54076
|
async onSessionStart(payload) {
|
|
53315
54077
|
const source = text(payload.source);
|
|
@@ -53333,20 +54095,26 @@ class LivePane {
|
|
|
53333
54095
|
return;
|
|
53334
54096
|
}
|
|
53335
54097
|
this.turn = {
|
|
53336
|
-
promptId: this.pendingPromptId ?? text(payload.prompt_id) ??
|
|
54098
|
+
promptId: this.pendingPromptId ?? text(payload.prompt_id) ?? randomUUID6()
|
|
53337
54099
|
};
|
|
53338
54100
|
this.pendingPromptId = undefined;
|
|
53339
54101
|
this.settled = false;
|
|
53340
54102
|
this.lastAssistantText = undefined;
|
|
53341
54103
|
this.lastStopReason = null;
|
|
54104
|
+
this.apiError = undefined;
|
|
53342
54105
|
this.queue.push(turnStarted(this.turn.promptId));
|
|
53343
54106
|
this.submitted.fire();
|
|
54107
|
+
const seconds = this.extras.deadlineSeconds;
|
|
54108
|
+
if (seconds !== undefined) {
|
|
54109
|
+
const promptId = this.turn.promptId;
|
|
54110
|
+
this.deadlineTimer = setTimeout(() => void this.guard("deadline", () => this.onDeadline(promptId, seconds)), seconds * this.daemon.timings.secondMs);
|
|
54111
|
+
}
|
|
53344
54112
|
}
|
|
53345
54113
|
onPreToolUse(payload) {
|
|
53346
54114
|
if (this.turn === undefined) {
|
|
53347
54115
|
return;
|
|
53348
54116
|
}
|
|
53349
|
-
const toolCallId = text(payload.tool_use_id) ??
|
|
54117
|
+
const toolCallId = text(payload.tool_use_id) ?? randomUUID6();
|
|
53350
54118
|
this.openTools.set(toolCallId, {
|
|
53351
54119
|
name: text(payload.tool_name),
|
|
53352
54120
|
input: payload.tool_input
|
|
@@ -53355,9 +54123,22 @@ class LivePane {
|
|
|
53355
54123
|
this.queue.push(toolCallBegin(payload, toolCallId));
|
|
53356
54124
|
}
|
|
53357
54125
|
async onPermissionRequest(payload, signal) {
|
|
54126
|
+
if (this.extras.permissionMode === "dontAsk") {
|
|
54127
|
+
this.daemon.logger.debug("tmuxd denied a permission request in dontAsk mode", {
|
|
54128
|
+
pane: this.id,
|
|
54129
|
+
tool: text(payload.tool_name)
|
|
54130
|
+
});
|
|
54131
|
+
return {
|
|
54132
|
+
ok: true,
|
|
54133
|
+
decision: {
|
|
54134
|
+
behavior: "deny",
|
|
54135
|
+
message: UNATTENDED_DENIAL
|
|
54136
|
+
}
|
|
54137
|
+
};
|
|
54138
|
+
}
|
|
53358
54139
|
const correlated = this.findOpenTool(payload);
|
|
53359
54140
|
const dialog = {
|
|
53360
|
-
toolCallId: correlated ??
|
|
54141
|
+
toolCallId: correlated ?? randomUUID6(),
|
|
53361
54142
|
correlated: correlated !== undefined,
|
|
53362
54143
|
toolName: text(payload.tool_name),
|
|
53363
54144
|
input: payload.tool_input,
|
|
@@ -53481,7 +54262,10 @@ class LivePane {
|
|
|
53481
54262
|
return;
|
|
53482
54263
|
}
|
|
53483
54264
|
await this.syncTranscript();
|
|
53484
|
-
this.closeTurn(turnCompleted(apiStopReason(this.lastStopReason), text(payload.last_assistant_message) ?? this.lastAssistantText), "stop");
|
|
54265
|
+
this.closeTurn(this.endOfTurn(turnCompleted(apiStopReason(this.lastStopReason), text(payload.last_assistant_message) ?? this.lastAssistantText ?? "")), "stop");
|
|
54266
|
+
}
|
|
54267
|
+
endOfTurn(completed) {
|
|
54268
|
+
return this.apiError === undefined ? completed : turnFailedWith(this.apiError.reason, this.apiError.detail);
|
|
53485
54269
|
}
|
|
53486
54270
|
async onStopFailure(payload) {
|
|
53487
54271
|
if (this.turn === undefined) {
|
|
@@ -53535,13 +54319,19 @@ class LivePane {
|
|
|
53535
54319
|
}
|
|
53536
54320
|
this.lastActivityAt = Date.now();
|
|
53537
54321
|
if (classifyInterrupt(record) !== null) {
|
|
53538
|
-
|
|
54322
|
+
if (this.interrupt === undefined) {
|
|
54323
|
+
this.closeTurn(this.interruptedTurn(), "marker");
|
|
54324
|
+
} else {
|
|
54325
|
+
this.interrupt.marked = true;
|
|
54326
|
+
this.settleInterrupt();
|
|
54327
|
+
}
|
|
53539
54328
|
this.clearDialog();
|
|
53540
54329
|
continue;
|
|
53541
54330
|
}
|
|
54331
|
+
const failure = classifyApiError(record);
|
|
53542
54332
|
for (const block of assistantBlocks(record)) {
|
|
53543
54333
|
this.queue.push(block.kind === "text" ? outputChunk(block.text) : thinkingChunk(block.text));
|
|
53544
|
-
if (block.kind === "text") {
|
|
54334
|
+
if (block.kind === "text" && failure === null) {
|
|
53545
54335
|
this.lastAssistantText = block.text;
|
|
53546
54336
|
}
|
|
53547
54337
|
}
|
|
@@ -53555,6 +54345,14 @@ class LivePane {
|
|
|
53555
54345
|
} else if (record.type === "user") {
|
|
53556
54346
|
this.settled = false;
|
|
53557
54347
|
}
|
|
54348
|
+
if (failure !== null) {
|
|
54349
|
+
this.apiError = failure;
|
|
54350
|
+
if (failure.reason === "usage_limit") {
|
|
54351
|
+
this.closeTurn(turnFailedWith(failure.reason, failure.detail), "api_error");
|
|
54352
|
+
}
|
|
54353
|
+
} else if (record.type === "assistant" || record.type === "user" && record.isMeta !== true) {
|
|
54354
|
+
this.apiError = undefined;
|
|
54355
|
+
}
|
|
53558
54356
|
}
|
|
53559
54357
|
}
|
|
53560
54358
|
async monitor() {
|
|
@@ -53563,21 +54361,33 @@ class LivePane {
|
|
|
53563
54361
|
}
|
|
53564
54362
|
this.monitoring = true;
|
|
53565
54363
|
try {
|
|
53566
|
-
|
|
54364
|
+
const alive = await this.alive();
|
|
54365
|
+
if (this.closed) {
|
|
54366
|
+
return;
|
|
54367
|
+
}
|
|
54368
|
+
if (!alive) {
|
|
53567
54369
|
await this.onDeath();
|
|
53568
54370
|
return;
|
|
53569
54371
|
}
|
|
53570
54372
|
const quiet = Date.now() - this.lastActivityAt >= this.daemon.timings.watchdogQuietMs;
|
|
53571
54373
|
const idle = this.dialog === undefined && this.openTools.size === 0 && !this.compacting && this.settled;
|
|
53572
54374
|
if (this.turn !== undefined && quiet && idle) {
|
|
53573
|
-
this.closeTurn(turnCompleted("watchdog", this.lastAssistantText), "watchdog");
|
|
54375
|
+
this.closeTurn(this.endOfTurn(turnCompleted("watchdog", this.lastAssistantText ?? "")), "watchdog");
|
|
54376
|
+
}
|
|
54377
|
+
const idleTimeoutMs = this.daemon.timings.idleTimeoutMs;
|
|
54378
|
+
const unoccupied = this.turn === undefined && this.pendingPromptId === undefined && this.dialog === undefined && !this.compacting && !this.isRetaining;
|
|
54379
|
+
if (idleTimeoutMs > 0 && unoccupied && Date.now() - this.lastActivityAt >= idleTimeoutMs) {
|
|
54380
|
+
await this.end("idle_timeout");
|
|
53574
54381
|
}
|
|
53575
54382
|
} finally {
|
|
53576
54383
|
this.monitoring = false;
|
|
53577
54384
|
}
|
|
53578
54385
|
}
|
|
53579
54386
|
async beat() {
|
|
53580
|
-
if (
|
|
54387
|
+
if (this.closed || this.isRetaining || !await this.alive()) {
|
|
54388
|
+
return;
|
|
54389
|
+
}
|
|
54390
|
+
if (!this.closed && !this.isRetaining) {
|
|
53581
54391
|
this.queue.push(heartbeat());
|
|
53582
54392
|
}
|
|
53583
54393
|
}
|
|
@@ -53588,28 +54398,32 @@ class LivePane {
|
|
|
53588
54398
|
if (this.closed) {
|
|
53589
54399
|
return;
|
|
53590
54400
|
}
|
|
53591
|
-
|
|
53592
|
-
|
|
53593
|
-
|
|
53594
|
-
this.
|
|
53595
|
-
|
|
53596
|
-
|
|
53597
|
-
|
|
53598
|
-
|
|
53599
|
-
|
|
54401
|
+
await this.tearDown(async () => {
|
|
54402
|
+
const turnRunning = this.turn !== undefined;
|
|
54403
|
+
this.close();
|
|
54404
|
+
if (this.sessionEndReason !== undefined) {
|
|
54405
|
+
this.queue.push(sessionEnded("stopped"));
|
|
54406
|
+
} else {
|
|
54407
|
+
this.queue.push(sessionEnded("crashed"));
|
|
54408
|
+
this.queue.push(processDied());
|
|
54409
|
+
if (turnRunning) {
|
|
54410
|
+
this.queue.push(sessionFailed("process_died", "claude exited while a turn was running."));
|
|
54411
|
+
}
|
|
53600
54412
|
}
|
|
53601
|
-
|
|
53602
|
-
|
|
53603
|
-
|
|
53604
|
-
|
|
54413
|
+
await this.queue.drain(STOP_DRAIN_MS);
|
|
54414
|
+
this.queue.stop();
|
|
54415
|
+
this.daemon.remove(this);
|
|
54416
|
+
});
|
|
53605
54417
|
}
|
|
53606
54418
|
async abandon() {
|
|
53607
54419
|
if (this.closed) {
|
|
53608
54420
|
return;
|
|
53609
54421
|
}
|
|
53610
|
-
this.
|
|
53611
|
-
|
|
53612
|
-
|
|
54422
|
+
await this.tearDown(async () => {
|
|
54423
|
+
this.close();
|
|
54424
|
+
await this.killLogged();
|
|
54425
|
+
this.daemon.remove(this);
|
|
54426
|
+
});
|
|
53613
54427
|
}
|
|
53614
54428
|
async killLogged() {
|
|
53615
54429
|
try {
|
|
@@ -53639,6 +54453,7 @@ class LiveDaemon {
|
|
|
53639
54453
|
logger;
|
|
53640
54454
|
timings;
|
|
53641
54455
|
panes = new Map;
|
|
54456
|
+
starting = new Set;
|
|
53642
54457
|
server;
|
|
53643
54458
|
constructor(options) {
|
|
53644
54459
|
this.options = options;
|
|
@@ -53656,7 +54471,7 @@ class LiveDaemon {
|
|
|
53656
54471
|
}
|
|
53657
54472
|
async listen() {
|
|
53658
54473
|
const { socketPath } = this.options;
|
|
53659
|
-
await mkdir4(
|
|
54474
|
+
await mkdir4(dirname5(socketPath), { recursive: true, mode: 448 });
|
|
53660
54475
|
if (await Bun.file(socketPath).exists()) {
|
|
53661
54476
|
if (await this.answering(socketPath)) {
|
|
53662
54477
|
throw new Error(`a tmuxd daemon already listens on ${socketPath}`);
|
|
@@ -53724,52 +54539,108 @@ class LiveDaemon {
|
|
|
53724
54539
|
}
|
|
53725
54540
|
}
|
|
53726
54541
|
async start(args) {
|
|
53727
|
-
|
|
53728
|
-
if (envFile === undefined || !isAbsolute4(envFile)) {
|
|
53729
|
-
return refuse("usage_error");
|
|
53730
|
-
}
|
|
54542
|
+
let result;
|
|
53731
54543
|
try {
|
|
53732
|
-
|
|
54544
|
+
result = await this.launch(args);
|
|
54545
|
+
return result;
|
|
53733
54546
|
} finally {
|
|
53734
|
-
await
|
|
54547
|
+
await removeHandoff(args, result?.ok === true, this.logger);
|
|
53735
54548
|
}
|
|
53736
54549
|
}
|
|
53737
|
-
async launch(args
|
|
54550
|
+
async launch(args) {
|
|
53738
54551
|
const { pane: id, cwd, model, resume } = args;
|
|
54552
|
+
const envFile = args["env-file"];
|
|
53739
54553
|
const settingsBase = args["settings-base"];
|
|
54554
|
+
const policyFile = args["tool-policy"];
|
|
54555
|
+
const systemPromptFile = args["system-prompt-file"];
|
|
54556
|
+
const mcpConfig = args["mcp-config"];
|
|
54557
|
+
const featuresFile = args.features;
|
|
53740
54558
|
if (id === undefined || !PANE_ID2.test(id) || cwd === undefined || !isAbsolute4(cwd)) {
|
|
53741
54559
|
return refuse("usage_error");
|
|
53742
54560
|
}
|
|
53743
|
-
if (settingsBase === undefined || !isAbsolute4(settingsBase)) {
|
|
54561
|
+
if (envFile === undefined || !isAbsolute4(envFile) || settingsBase === undefined || !isAbsolute4(settingsBase)) {
|
|
53744
54562
|
return refuse("usage_error");
|
|
53745
54563
|
}
|
|
53746
54564
|
if (model !== undefined && !MODEL_NAME.test(model)) {
|
|
53747
54565
|
return refuse("usage_error");
|
|
53748
54566
|
}
|
|
53749
|
-
|
|
54567
|
+
const extras = parseStartExtras(args);
|
|
54568
|
+
if (extras === null || [policyFile, systemPromptFile, mcpConfig, featuresFile].some((path) => !optionalAbsolute(path))) {
|
|
54569
|
+
return refuse("usage_error");
|
|
54570
|
+
}
|
|
54571
|
+
if (this.panes.has(id) || this.starting.has(id)) {
|
|
53750
54572
|
return refuse("pane_exists");
|
|
53751
54573
|
}
|
|
53752
|
-
if (this.panes.size >= (this.options.maxPanes ?? DEFAULT_MAX_PANES)) {
|
|
54574
|
+
if (this.panes.size + this.starting.size >= (this.options.maxPanes ?? DEFAULT_MAX_PANES)) {
|
|
53753
54575
|
return refuse("pane_cap");
|
|
53754
54576
|
}
|
|
54577
|
+
this.starting.add(id);
|
|
54578
|
+
try {
|
|
54579
|
+
return await this.open({
|
|
54580
|
+
id,
|
|
54581
|
+
cwd,
|
|
54582
|
+
model,
|
|
54583
|
+
resume,
|
|
54584
|
+
envFile,
|
|
54585
|
+
settingsBase,
|
|
54586
|
+
policyFile,
|
|
54587
|
+
systemPromptFile,
|
|
54588
|
+
mcpConfig,
|
|
54589
|
+
featuresFile,
|
|
54590
|
+
extras
|
|
54591
|
+
});
|
|
54592
|
+
} finally {
|
|
54593
|
+
this.starting.delete(id);
|
|
54594
|
+
}
|
|
54595
|
+
}
|
|
54596
|
+
async open(request) {
|
|
54597
|
+
const {
|
|
54598
|
+
id,
|
|
54599
|
+
cwd,
|
|
54600
|
+
model,
|
|
54601
|
+
resume,
|
|
54602
|
+
envFile,
|
|
54603
|
+
settingsBase,
|
|
54604
|
+
policyFile,
|
|
54605
|
+
systemPromptFile,
|
|
54606
|
+
mcpConfig,
|
|
54607
|
+
featuresFile,
|
|
54608
|
+
extras
|
|
54609
|
+
} = request;
|
|
53755
54610
|
const content = await readSecretFile(envFile);
|
|
53756
54611
|
const env = content === null ? null : parseStartEnv(content);
|
|
53757
54612
|
if (env === null) {
|
|
53758
54613
|
return refuse("usage_error");
|
|
53759
54614
|
}
|
|
54615
|
+
for (const path of [systemPromptFile, mcpConfig]) {
|
|
54616
|
+
if (path !== undefined && !await isPrivateFile(path)) {
|
|
54617
|
+
return refuse("usage_error");
|
|
54618
|
+
}
|
|
54619
|
+
}
|
|
54620
|
+
const policy = policyFile === undefined ? undefined : await readToolPolicy(policyFile);
|
|
54621
|
+
if (policy === null) {
|
|
54622
|
+
return refuse("usage_error");
|
|
54623
|
+
}
|
|
54624
|
+
const features = featuresFile === undefined ? undefined : await readPaneFeatures(featuresFile, extras.permissionMode);
|
|
54625
|
+
if (features === null) {
|
|
54626
|
+
return refuse("usage_error");
|
|
54627
|
+
}
|
|
54628
|
+
const mcpSpellings = mcpConfig === undefined ? [] : [...new Set([mcpConfig, await realpath4(mcpConfig)])];
|
|
53760
54629
|
const settingsPath = join7(settingsBase, `${id}.settings.json`);
|
|
53761
54630
|
await mkdir4(settingsBase, { recursive: true, mode: 448 });
|
|
53762
54631
|
await writePaneSettings(settingsPath, {
|
|
53763
54632
|
basePath: this.options.baseSettingsPath ?? DEFAULT_BASE_SETTINGS_PATH,
|
|
53764
54633
|
helperArgv: this.options.helperArgv,
|
|
53765
54634
|
socketPath: this.options.socketPath,
|
|
53766
|
-
paneId: id
|
|
54635
|
+
paneId: id,
|
|
54636
|
+
rules: paneRules(policy, mcpSpellings, extras.permissionMode)
|
|
53767
54637
|
});
|
|
53768
54638
|
const pane = {
|
|
53769
54639
|
id,
|
|
53770
54640
|
socket: this.options.tmuxSocket ?? LIVE_SOCKET
|
|
53771
54641
|
};
|
|
53772
|
-
const live = new LivePane(pane, env.gen, env.ingestUrl, env.token, Object.values(env.authEnv), this);
|
|
54642
|
+
const live = new LivePane(pane, env.gen, env.ingestUrl, env.token, Object.values(env.authEnv), this, extras, mcpConfig, settingsBase);
|
|
54643
|
+
this.starting.delete(id);
|
|
53773
54644
|
this.panes.set(id, live);
|
|
53774
54645
|
try {
|
|
53775
54646
|
await createPane({
|
|
@@ -53777,9 +54648,14 @@ class LiveDaemon {
|
|
|
53777
54648
|
cwd,
|
|
53778
54649
|
home: this.options.home,
|
|
53779
54650
|
settingsPath,
|
|
53780
|
-
env: env.authEnv,
|
|
54651
|
+
env: { ...env.authEnv, ...features?.env },
|
|
53781
54652
|
model,
|
|
53782
54653
|
resumeSessionId: resume,
|
|
54654
|
+
mcpConfig,
|
|
54655
|
+
systemPromptFile,
|
|
54656
|
+
tools: features?.tools,
|
|
54657
|
+
disallowedTools: features?.disallowedTools,
|
|
54658
|
+
permissionMode: extras.permissionMode,
|
|
53783
54659
|
claudeBin: this.options.claudeBin,
|
|
53784
54660
|
socket: pane.socket,
|
|
53785
54661
|
confirmStarted: (timeoutMs) => live.started.wait(timeoutMs)
|
|
@@ -53789,7 +54665,13 @@ class LiveDaemon {
|
|
|
53789
54665
|
live.queue.stop();
|
|
53790
54666
|
return refuse(launchFailure(error));
|
|
53791
54667
|
}
|
|
53792
|
-
|
|
54668
|
+
try {
|
|
54669
|
+
live.pid = await panePid(pane);
|
|
54670
|
+
} catch (error) {
|
|
54671
|
+
this.panes.delete(id);
|
|
54672
|
+
await live.dispose();
|
|
54673
|
+
return refuse(launchFailure(error));
|
|
54674
|
+
}
|
|
53793
54675
|
live.begin();
|
|
53794
54676
|
return accept({
|
|
53795
54677
|
pane: id,
|
|
@@ -53825,7 +54707,7 @@ class LiveDaemon {
|
|
|
53825
54707
|
}
|
|
53826
54708
|
throw error;
|
|
53827
54709
|
}
|
|
53828
|
-
const promptId =
|
|
54710
|
+
const promptId = randomUUID6();
|
|
53829
54711
|
live.pendingPromptId = promptId;
|
|
53830
54712
|
live.submitted.arm();
|
|
53831
54713
|
let outcome;
|
|
@@ -53971,7 +54853,7 @@ class LiveDaemon {
|
|
|
53971
54853
|
if (live.dialog !== undefined) {
|
|
53972
54854
|
return refuse("dialog_open");
|
|
53973
54855
|
}
|
|
53974
|
-
if (live.busy || live.pendingPromptId !== undefined) {
|
|
54856
|
+
if (live.busy || live.refusesInput || live.pendingPromptId !== undefined) {
|
|
53975
54857
|
return refuse("busy");
|
|
53976
54858
|
}
|
|
53977
54859
|
return null;
|
|
@@ -54083,17 +54965,69 @@ function parseStartEnv(content) {
|
|
|
54083
54965
|
authEnv: Object.fromEntries(Object.entries(values).filter(([key]) => !key.startsWith(LIVE_KEY_PREFIX)))
|
|
54084
54966
|
};
|
|
54085
54967
|
}
|
|
54086
|
-
|
|
54087
|
-
const
|
|
54968
|
+
function paneRules(policy, mcpPaths, mode) {
|
|
54969
|
+
const deny = [...mcpPaths.map((path) => `Read(/${path})`), ...mode === "dontAsk" ? ["EnterPlanMode"] : []];
|
|
54970
|
+
if (policy === undefined && deny.length === 0) {
|
|
54971
|
+
return;
|
|
54972
|
+
}
|
|
54973
|
+
return {
|
|
54974
|
+
allow: policy?.allow ?? [],
|
|
54975
|
+
deny: [...policy?.deny ?? [], ...deny],
|
|
54976
|
+
ask: policy?.ask ?? []
|
|
54977
|
+
};
|
|
54978
|
+
}
|
|
54979
|
+
function optionalAbsolute(path) {
|
|
54980
|
+
return path === undefined || isAbsolute4(path);
|
|
54981
|
+
}
|
|
54982
|
+
async function removeHandoff(args, keepMcp, logger) {
|
|
54983
|
+
const files = START_FILE_FLAGS.flatMap((flag) => {
|
|
54984
|
+
const path = args[flag];
|
|
54985
|
+
return path === undefined || !isAbsolute4(path) || keepMcp && flag === "mcp-config" ? [] : [[flag, path]];
|
|
54986
|
+
});
|
|
54987
|
+
for (const failure of await removeHandoffFiles(files)) {
|
|
54988
|
+
logger.error("tmuxd could not remove a hand-off file", { ...failure });
|
|
54989
|
+
}
|
|
54990
|
+
}
|
|
54991
|
+
async function removeHandoffFiles(files) {
|
|
54992
|
+
const ordered = [
|
|
54993
|
+
...files.filter(([flag]) => flag === "mcp-config"),
|
|
54994
|
+
...files.filter(([flag]) => flag !== "mcp-config")
|
|
54995
|
+
];
|
|
54996
|
+
const failures = [];
|
|
54997
|
+
for (const [flag, path] of ordered) {
|
|
54998
|
+
try {
|
|
54999
|
+
await removeFile(path);
|
|
55000
|
+
} catch (error) {
|
|
55001
|
+
if (!isErrno(error)) {
|
|
55002
|
+
throw error;
|
|
55003
|
+
}
|
|
55004
|
+
failures.push({
|
|
55005
|
+
flag,
|
|
55006
|
+
code: error.code
|
|
55007
|
+
});
|
|
55008
|
+
}
|
|
55009
|
+
}
|
|
55010
|
+
return failures;
|
|
55011
|
+
}
|
|
55012
|
+
async function isPrivateFile(path) {
|
|
55013
|
+
const info = await lstat2(path).catch((error) => {
|
|
54088
55014
|
if (error.code === "ENOENT") {
|
|
54089
55015
|
return null;
|
|
54090
55016
|
}
|
|
54091
55017
|
throw error;
|
|
54092
55018
|
});
|
|
54093
|
-
|
|
54094
|
-
|
|
54095
|
-
|
|
54096
|
-
return readFile4(path, "utf8");
|
|
55019
|
+
return info?.isFile() === true && (info.mode & 511) === 384;
|
|
55020
|
+
}
|
|
55021
|
+
async function readSecretFile(path) {
|
|
55022
|
+
return await isPrivateFile(path) ? readFile4(path, "utf8") : null;
|
|
55023
|
+
}
|
|
55024
|
+
async function readToolPolicy(path) {
|
|
55025
|
+
const content = await readSecretFile(path);
|
|
55026
|
+
return content === null ? null : parseToolPolicy(content);
|
|
55027
|
+
}
|
|
55028
|
+
async function readPaneFeatures(path, permissionMode) {
|
|
55029
|
+
const content = await readSecretFile(path);
|
|
55030
|
+
return content === null ? null : parsePaneFeatures(content, permissionMode);
|
|
54097
55031
|
}
|
|
54098
55032
|
async function readFileOrNull(path) {
|
|
54099
55033
|
return readFile4(path, "utf8").catch((error) => {
|
|
@@ -54255,11 +55189,18 @@ function accept(data) {
|
|
|
54255
55189
|
return {
|
|
54256
55190
|
ok: true,
|
|
54257
55191
|
code: "ok",
|
|
54258
|
-
data:
|
|
55192
|
+
data: stamped(data)
|
|
54259
55193
|
};
|
|
54260
55194
|
}
|
|
54261
|
-
function refuse(code, data) {
|
|
54262
|
-
return
|
|
55195
|
+
function refuse(code, data = {}) {
|
|
55196
|
+
return {
|
|
55197
|
+
ok: false,
|
|
55198
|
+
code,
|
|
55199
|
+
data: stamped(data)
|
|
55200
|
+
};
|
|
55201
|
+
}
|
|
55202
|
+
function stamped(data) {
|
|
55203
|
+
return Object.fromEntries(Object.entries({ daemon_version: DAEMON_VERSION, ...data }).filter(([, value]) => value !== undefined));
|
|
54263
55204
|
}
|
|
54264
55205
|
function notRunning(id) {
|
|
54265
55206
|
return refuse("pane_not_running", {
|
|
@@ -54267,14 +55208,18 @@ function notRunning(id) {
|
|
|
54267
55208
|
running: false
|
|
54268
55209
|
});
|
|
54269
55210
|
}
|
|
54270
|
-
var HEARTBEAT_INTERVAL_MS = 20000, WATCHDOG_QUIET_MS = 30000, CANCEL_TIMEOUT_MS = 3000, PERMISSION_POLL_MS = 1500, INGEST_TIMEOUT_MS = 1e4, MAX_BATCH = 200, DEFAULT_MAX_PANES = 4, STOP_DRAIN_MS = 1e4, PERMISSION_WAIT_MS, PERMISSION_OPEN_RETRY_MS = 90000, MIN_SECRET_LENGTH = 8, MAX_REQUEST_BYTES, PANE_ID2, ENV_KEY, LIVE_KEY_PREFIX = "KODIZM_LIVE_", TOKEN_PREFIX = "kdz-int-jwt.", COMMAND_NAMES, REJECTION_RULES, EXIT_CODES, DEFAULT_TIMINGS;
|
|
55211
|
+
var DAEMON_VERSION, HEARTBEAT_INTERVAL_MS = 20000, WATCHDOG_QUIET_MS = 30000, CANCEL_TIMEOUT_MS = 3000, PERMISSION_POLL_MS = 1500, INGEST_TIMEOUT_MS = 1e4, MAX_BATCH = 200, DEFAULT_MAX_PANES = 4, STOP_DRAIN_MS = 1e4, PERMISSION_WAIT_MS, PERMISSION_OPEN_RETRY_MS = 90000, MIN_SECRET_LENGTH = 8, MAX_REQUEST_BYTES, PANE_ID2, ENV_KEY, LIVE_KEY_PREFIX = "KODIZM_LIVE_", TOKEN_PREFIX = "kdz-int-jwt.", LAST_MESSAGE_FILE = "last-assistant-message.txt", COMMAND_NAMES, UNATTENDED_DENIAL = "This session runs unattended and approves nothing, so the tool call is denied.", REJECTION_RULES, EXIT_CODES, DEFAULT_TIMINGS;
|
|
54271
55212
|
var init_daemon = __esm(() => {
|
|
55213
|
+
init_package();
|
|
54272
55214
|
init_logger();
|
|
54273
55215
|
init_events2();
|
|
54274
55216
|
init_hooks();
|
|
54275
55217
|
init_input();
|
|
54276
55218
|
init_pane();
|
|
55219
|
+
init_policy4();
|
|
55220
|
+
init_start_flags();
|
|
54277
55221
|
init_transcript();
|
|
55222
|
+
DAEMON_VERSION = package_default.version;
|
|
54278
55223
|
PERMISSION_WAIT_MS = PERMISSION_HOOK_TIMEOUT_S * 1000 - 1e4;
|
|
54279
55224
|
MAX_REQUEST_BYTES = 8 * 1024 * 1024;
|
|
54280
55225
|
PANE_ID2 = /^[A-Za-z0-9_-]{1,64}$/;
|
|
@@ -54308,14 +55253,17 @@ var init_daemon = __esm(() => {
|
|
|
54308
55253
|
monitorMs: 2000,
|
|
54309
55254
|
transcriptPollMs: 250,
|
|
54310
55255
|
retryBaseMs: 250,
|
|
54311
|
-
retryMaxMs: 1e4
|
|
55256
|
+
retryMaxMs: 1e4,
|
|
55257
|
+
secondMs: 1000,
|
|
55258
|
+
idleTimeoutMs: 0,
|
|
55259
|
+
deliveryDrainMs: 120000
|
|
54312
55260
|
};
|
|
54313
55261
|
});
|
|
54314
55262
|
|
|
54315
55263
|
// src/live/ctl.ts
|
|
54316
55264
|
import { spawn as spawn2 } from "child_process";
|
|
54317
55265
|
import { homedir as homedir2, tmpdir } from "os";
|
|
54318
|
-
import { resolve as
|
|
55266
|
+
import { resolve as resolve6 } from "path";
|
|
54319
55267
|
async function runTmuxd(args) {
|
|
54320
55268
|
const [command, ...rest] = args;
|
|
54321
55269
|
switch (command) {
|
|
@@ -54338,9 +55286,9 @@ async function runTmuxd(args) {
|
|
|
54338
55286
|
async function runCtl(args) {
|
|
54339
55287
|
const [verb, ...rest] = args;
|
|
54340
55288
|
const flags = parseFlags(rest);
|
|
54341
|
-
if (verb === undefined || !VERBS.has(verb) || flags === null) {
|
|
55289
|
+
if (verb === undefined || !VERBS.has(verb) || flags === null || verb === "start" && parseStartExtras(flags) === null) {
|
|
54342
55290
|
await discardHandoff(verb, rest);
|
|
54343
|
-
return print(
|
|
55291
|
+
return print(ctlMade("usage_error"));
|
|
54344
55292
|
}
|
|
54345
55293
|
const socketPath = socketPathFromEnv();
|
|
54346
55294
|
const message = {
|
|
@@ -54359,7 +55307,7 @@ async function runCtl(args) {
|
|
|
54359
55307
|
if (error.reason === "unreachable") {
|
|
54360
55308
|
await discardHandoff(verb, rest);
|
|
54361
55309
|
}
|
|
54362
|
-
return print(error.reason === "unreachable" ?
|
|
55310
|
+
return print(error.reason === "unreachable" ? ctlMade("pane_not_running", { pane: flags.pane, running: false }) : ctlMade("timeout"));
|
|
54363
55311
|
}
|
|
54364
55312
|
}
|
|
54365
55313
|
async function ask(socketPath, message, spawnIfMissing) {
|
|
@@ -54426,6 +55374,7 @@ async function runDaemon() {
|
|
|
54426
55374
|
baseSettingsPath: env.KODIZM_LIVE_BASE_SETTINGS ?? DEFAULT_BASE_SETTINGS_PATH,
|
|
54427
55375
|
maxPanes: Number.isInteger(maxPanes) && maxPanes > 0 ? maxPanes : DEFAULT_MAX_PANES,
|
|
54428
55376
|
logger: createLogger({ env }),
|
|
55377
|
+
timings: { idleTimeoutMs: idleTimeoutMs(env) },
|
|
54429
55378
|
onIdle: () => void daemon.close().then(shutdown)
|
|
54430
55379
|
});
|
|
54431
55380
|
await daemon.listen();
|
|
@@ -54435,6 +55384,10 @@ async function runDaemon() {
|
|
|
54435
55384
|
await stopped;
|
|
54436
55385
|
return 0;
|
|
54437
55386
|
}
|
|
55387
|
+
function idleTimeoutMs(env) {
|
|
55388
|
+
const seconds = env.KODIZM_TMUXD_IDLE_TIMEOUT_SECONDS;
|
|
55389
|
+
return seconds !== undefined && /^[1-9]\d*$/.test(seconds) ? Number(seconds) * 1000 : 0;
|
|
55390
|
+
}
|
|
54438
55391
|
function spawnDaemon() {
|
|
54439
55392
|
const child = spawn2(process.execPath, [process.argv[1] ?? "", "tmuxd", "daemon"], {
|
|
54440
55393
|
detached: true,
|
|
@@ -54455,21 +55408,37 @@ function parseFlags(args) {
|
|
|
54455
55408
|
if (!flag.startsWith("--") || name === "" || value === undefined || name in flags) {
|
|
54456
55409
|
return null;
|
|
54457
55410
|
}
|
|
54458
|
-
flags[name] = PATH_FLAGS.has(name) ?
|
|
55411
|
+
flags[name] = PATH_FLAGS.has(name) ? resolve6(value) : value;
|
|
54459
55412
|
}
|
|
54460
55413
|
return flags;
|
|
54461
55414
|
}
|
|
54462
55415
|
async function discardHandoff(verb, args) {
|
|
54463
|
-
const
|
|
54464
|
-
if (
|
|
55416
|
+
const flags = verb === undefined ? undefined : HANDOFF_FLAGS.get(verb);
|
|
55417
|
+
if (flags === undefined) {
|
|
54465
55418
|
return;
|
|
54466
55419
|
}
|
|
55420
|
+
const files = [];
|
|
54467
55421
|
for (let index = 0;index < args.length - 1; index += 1) {
|
|
54468
55422
|
const value = args[index + 1];
|
|
54469
|
-
|
|
54470
|
-
|
|
55423
|
+
const flag = flags.find((name) => args[index] === `--${name}`);
|
|
55424
|
+
if (value !== undefined && flag !== undefined) {
|
|
55425
|
+
files.push([flag, resolve6(value)]);
|
|
54471
55426
|
}
|
|
54472
55427
|
}
|
|
55428
|
+
for (const failure of await removeHandoffFiles(files)) {
|
|
55429
|
+
process.stderr.write(`${JSON.stringify({ level: "error", message: "tmuxd ctl could not remove a hand-off file", ...failure })}
|
|
55430
|
+
`);
|
|
55431
|
+
}
|
|
55432
|
+
}
|
|
55433
|
+
function ctlMade(code, data = {}) {
|
|
55434
|
+
return {
|
|
55435
|
+
ok: false,
|
|
55436
|
+
code,
|
|
55437
|
+
data: {
|
|
55438
|
+
daemon_version: DAEMON_VERSION,
|
|
55439
|
+
...data
|
|
55440
|
+
}
|
|
55441
|
+
};
|
|
54473
55442
|
}
|
|
54474
55443
|
function toResult(response) {
|
|
54475
55444
|
const code = typeof response === "object" && response !== null ? response.code : undefined;
|
|
@@ -54498,11 +55467,15 @@ var init_ctl = __esm(() => {
|
|
|
54498
55467
|
init_daemon();
|
|
54499
55468
|
init_hooks();
|
|
54500
55469
|
init_pane();
|
|
55470
|
+
init_start_flags();
|
|
54501
55471
|
USAGE = `Usage: kodizm-acp tmuxd <command>
|
|
54502
55472
|
|
|
54503
55473
|
Commands:
|
|
54504
55474
|
ctl <verb> [flags] Drive a Live pane through the daemon; prints one JSON line.
|
|
54505
55475
|
Verbs: start, prompt, command, cancel, stop, rotate-token, status.
|
|
55476
|
+
start also takes --permission-mode default|dontAsk, --one-shot 0|1,
|
|
55477
|
+
--deadline-seconds N, --retain-failed-seconds N and
|
|
55478
|
+
--mcp-config/--tool-policy/--system-prompt-file/--features PATH.
|
|
54506
55479
|
hook <Event> --pane <id> --socket <path> Hook helper the pane settings run; reads the payload on stdin.
|
|
54507
55480
|
daemon Run the daemon in the foreground; ctl start spawns it detached.
|
|
54508
55481
|
|
|
@@ -54510,82 +55483,30 @@ Environment:
|
|
|
54510
55483
|
KODIZM_TMUXD_SOCKET daemon socket (default: $XDG_RUNTIME_DIR or the temp dir, kodizm-live-<uid>/tmuxd.sock)
|
|
54511
55484
|
KODIZM_TMUXD_TMUX_SOCKET tmux socket name (default: ${LIVE_SOCKET})
|
|
54512
55485
|
KODIZM_TMUXD_MAX_PANES panes per daemon (default: ${DEFAULT_MAX_PANES})
|
|
55486
|
+
KODIZM_TMUXD_IDLE_TIMEOUT_SECONDS
|
|
55487
|
+
end a pane with no turn, dialog or activity for N seconds (default: off)
|
|
54513
55488
|
KODIZM_LIVE_BASE_SETTINGS base settings with the deny list (default: ${DEFAULT_BASE_SETTINGS_PATH})
|
|
54514
55489
|
CLAUDE_CODE_PATH claude binary (default: claude on PATH)
|
|
54515
55490
|
`;
|
|
54516
55491
|
VERBS = new Set(["start", "prompt", "command", "cancel", "stop", "rotate-token", "status"]);
|
|
54517
|
-
PATH_FLAGS = new Set([
|
|
54518
|
-
|
|
54519
|
-
|
|
54520
|
-
|
|
54521
|
-
"
|
|
54522
|
-
|
|
55492
|
+
PATH_FLAGS = new Set([
|
|
55493
|
+
"settings-base",
|
|
55494
|
+
"env-file",
|
|
55495
|
+
"file",
|
|
55496
|
+
"token-file",
|
|
55497
|
+
"mcp-config",
|
|
55498
|
+
"tool-policy",
|
|
55499
|
+
"system-prompt-file"
|
|
55500
|
+
]);
|
|
55501
|
+
HANDOFF_FLAGS = new Map([
|
|
55502
|
+
["start", START_FILE_FLAGS],
|
|
55503
|
+
["prompt", ["file"]],
|
|
55504
|
+
["rotate-token", ["token-file"]]
|
|
55505
|
+
]);
|
|
54523
55506
|
});
|
|
54524
|
-
|
|
54525
|
-
|
|
54526
|
-
|
|
54527
|
-
version: "0.8.2",
|
|
54528
|
-
description: "Custom ACP server bridging Claude Code and opencode CLIs through a single Kodizm-flavored protocol surface.",
|
|
54529
|
-
type: "module",
|
|
54530
|
-
bin: {
|
|
54531
|
-
"kodizm-acp": "./dist/index.js"
|
|
54532
|
-
},
|
|
54533
|
-
main: "./dist/index.js",
|
|
54534
|
-
files: [
|
|
54535
|
-
"dist/",
|
|
54536
|
-
"README.md",
|
|
54537
|
-
"LICENSE"
|
|
54538
|
-
],
|
|
54539
|
-
scripts: {
|
|
54540
|
-
dev: "bun run src/index.ts",
|
|
54541
|
-
build: "bun build src/index.ts --target=bun --outdir=dist",
|
|
54542
|
-
test: "bun test test/unit test/e2e",
|
|
54543
|
-
"test:unit": "bun test test/unit",
|
|
54544
|
-
"test:e2e": "bun test test/e2e",
|
|
54545
|
-
"test:integration": "./scripts/reap-opencode.sh bun test test/integration",
|
|
54546
|
-
"test:watch": "bun test --watch",
|
|
54547
|
-
lint: "biome check src test",
|
|
54548
|
-
"lint:fix": "biome check --write src test",
|
|
54549
|
-
format: "biome format --write src test",
|
|
54550
|
-
typecheck: "tsc --noEmit",
|
|
54551
|
-
"typecheck:src": "tsc --noEmit -p tsconfig.publish.json",
|
|
54552
|
-
prepublishOnly: "bun run build && bun run typecheck:src"
|
|
54553
|
-
},
|
|
54554
|
-
engines: {
|
|
54555
|
-
bun: ">=1.1.0"
|
|
54556
|
-
},
|
|
54557
|
-
publishConfig: {
|
|
54558
|
-
access: "public"
|
|
54559
|
-
},
|
|
54560
|
-
config: {
|
|
54561
|
-
claudeAgentSdk: "0.3.280"
|
|
54562
|
-
},
|
|
54563
|
-
devDependencies: {
|
|
54564
|
-
"@anthropic-ai/claude-agent-sdk": "0.3.280",
|
|
54565
|
-
"@anthropic-ai/sdk": ">=0.93.0",
|
|
54566
|
-
"@biomejs/biome": "^1.9.0",
|
|
54567
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
54568
|
-
"@opencode-ai/sdk": "^1.18.32",
|
|
54569
|
-
"@types/bun": "latest",
|
|
54570
|
-
typescript: "^5.6.0",
|
|
54571
|
-
zod: "^4.0.0"
|
|
54572
|
-
},
|
|
54573
|
-
license: "Apache-2.0",
|
|
54574
|
-
homepage: "https://github.com/kodizm/acp",
|
|
54575
|
-
repository: {
|
|
54576
|
-
type: "git",
|
|
54577
|
-
url: "git+https://github.com/kodizm/acp.git"
|
|
54578
|
-
},
|
|
54579
|
-
keywords: [
|
|
54580
|
-
"acp",
|
|
54581
|
-
"agent-client-protocol",
|
|
54582
|
-
"claude",
|
|
54583
|
-
"opencode",
|
|
54584
|
-
"ai",
|
|
54585
|
-
"bridge",
|
|
54586
|
-
"jsonrpc"
|
|
54587
|
-
]
|
|
54588
|
-
};
|
|
55507
|
+
|
|
55508
|
+
// src/index.ts
|
|
55509
|
+
init_package();
|
|
54589
55510
|
|
|
54590
55511
|
// src/backends/driver.ts
|
|
54591
55512
|
init_errors();
|
|
@@ -54602,151 +55523,8 @@ function ensureCapability(caps, required, method) {
|
|
|
54602
55523
|
throw new MethodNotSupportedError(method, supported);
|
|
54603
55524
|
}
|
|
54604
55525
|
|
|
54605
|
-
// src/wire/schemas.ts
|
|
54606
|
-
init_zod();
|
|
54607
|
-
var AbsolutePathSchema = string2().regex(/^\//, 'path must be absolute (start with "/")');
|
|
54608
|
-
var PLUGIN_ROOT = "/opt/kodizm/plugins/";
|
|
54609
|
-
var PluginPathSchema = AbsolutePathSchema.refine((path) => path.length > PLUGIN_ROOT.length && path.startsWith(PLUGIN_ROOT) && !path.split("/").includes(".."), { message: `plugin path must live under ${PLUGIN_ROOT} with no ".." segment` });
|
|
54610
|
-
var McpServerSchema = object({
|
|
54611
|
-
type: literal("http"),
|
|
54612
|
-
name: string2().min(1),
|
|
54613
|
-
url: string2().url(),
|
|
54614
|
-
headers: array(object({
|
|
54615
|
-
name: string2().min(1),
|
|
54616
|
-
value: string2()
|
|
54617
|
-
})).optional()
|
|
54618
|
-
});
|
|
54619
|
-
var SystemPromptSchema = union([
|
|
54620
|
-
string2(),
|
|
54621
|
-
object({
|
|
54622
|
-
append: string2(),
|
|
54623
|
-
excludeDynamicSections: boolean2().optional()
|
|
54624
|
-
})
|
|
54625
|
-
]);
|
|
54626
|
-
var MetaSchema = record(string2(), unknown());
|
|
54627
|
-
var CANONICAL_FIELDS_BANNED_IN_META = [
|
|
54628
|
-
"systemPrompt",
|
|
54629
|
-
"additionalDirectories",
|
|
54630
|
-
"mcpServers",
|
|
54631
|
-
"model",
|
|
54632
|
-
"skills",
|
|
54633
|
-
"cwd",
|
|
54634
|
-
"sessionId",
|
|
54635
|
-
"sourceSessionId",
|
|
54636
|
-
"toolPolicy",
|
|
54637
|
-
"autoCompact",
|
|
54638
|
-
"permissionTimeoutMs",
|
|
54639
|
-
"permissionDeferTimeoutMs",
|
|
54640
|
-
"debug",
|
|
54641
|
-
"debugCaptureRawSdk",
|
|
54642
|
-
"debugCaptureRpc",
|
|
54643
|
-
"heartbeatIntervalMs",
|
|
54644
|
-
"inactivityThresholdMs",
|
|
54645
|
-
"settingSources",
|
|
54646
|
-
"features",
|
|
54647
|
-
"plugins"
|
|
54648
|
-
];
|
|
54649
|
-
var SettingSourcesSchema = array(_enum(["user", "project", "local"]));
|
|
54650
|
-
var PermissionModeSchema = _enum(["default", "acceptEdits", "plan", "dontAsk", "bypassPermissions"]);
|
|
54651
|
-
var ToolPolicySchema = object({
|
|
54652
|
-
allow: array(string2()).optional(),
|
|
54653
|
-
deny: array(string2()).optional(),
|
|
54654
|
-
ask: array(string2()).optional(),
|
|
54655
|
-
defaultMode: PermissionModeSchema.optional()
|
|
54656
|
-
});
|
|
54657
|
-
var FeaturesSchema = object({
|
|
54658
|
-
todos: boolean2().optional(),
|
|
54659
|
-
subagents: boolean2().optional(),
|
|
54660
|
-
backgroundTasks: boolean2().optional(),
|
|
54661
|
-
scheduling: boolean2().optional(),
|
|
54662
|
-
planMode: boolean2().optional(),
|
|
54663
|
-
worktree: boolean2().optional(),
|
|
54664
|
-
notebook: boolean2().optional(),
|
|
54665
|
-
clientTools: boolean2().optional(),
|
|
54666
|
-
autoMemory: boolean2().optional(),
|
|
54667
|
-
bundledSkills: boolean2().optional(),
|
|
54668
|
-
simple: boolean2().optional(),
|
|
54669
|
-
tools: array(string2().min(1)).optional()
|
|
54670
|
-
});
|
|
54671
|
-
function metaCarriesNoCanonicalFields(data) {
|
|
54672
|
-
if (data._meta === undefined) {
|
|
54673
|
-
return true;
|
|
54674
|
-
}
|
|
54675
|
-
for (const banned of CANONICAL_FIELDS_BANNED_IN_META) {
|
|
54676
|
-
if (banned in data._meta) {
|
|
54677
|
-
return false;
|
|
54678
|
-
}
|
|
54679
|
-
}
|
|
54680
|
-
return true;
|
|
54681
|
-
}
|
|
54682
|
-
var META_SMUGGLE_MESSAGE = "canonical fields (systemPrompt, additionalDirectories, mcpServers, model, skills, cwd) must be top-level, not nested in _meta";
|
|
54683
|
-
var InitializeRequestSchema = object({
|
|
54684
|
-
protocolVersion: number2().int().positive(),
|
|
54685
|
-
clientCapabilities: record(string2(), unknown()).optional(),
|
|
54686
|
-
_meta: MetaSchema.optional()
|
|
54687
|
-
});
|
|
54688
|
-
var SessionOptionFields = {
|
|
54689
|
-
additionalDirectories: array(AbsolutePathSchema).optional(),
|
|
54690
|
-
plugins: array(PluginPathSchema).optional(),
|
|
54691
|
-
systemPrompt: SystemPromptSchema.optional(),
|
|
54692
|
-
model: string2().optional(),
|
|
54693
|
-
skills: array(string2()).optional(),
|
|
54694
|
-
toolPolicy: ToolPolicySchema.optional(),
|
|
54695
|
-
autoCompact: boolean2().optional(),
|
|
54696
|
-
permissionTimeoutMs: number2().int().positive().optional(),
|
|
54697
|
-
permissionDeferTimeoutMs: number2().int().positive().optional(),
|
|
54698
|
-
debug: boolean2().optional(),
|
|
54699
|
-
debugCaptureRawSdk: boolean2().optional(),
|
|
54700
|
-
debugCaptureRpc: boolean2().optional(),
|
|
54701
|
-
heartbeatIntervalMs: number2().int().positive().optional(),
|
|
54702
|
-
inactivityThresholdMs: number2().int().positive().optional(),
|
|
54703
|
-
settingSources: SettingSourcesSchema.optional(),
|
|
54704
|
-
features: FeaturesSchema.optional(),
|
|
54705
|
-
_meta: MetaSchema.optional()
|
|
54706
|
-
};
|
|
54707
|
-
var TIMEOUT_EXCLUSION = {
|
|
54708
|
-
message: "permissionTimeoutMs and permissionDeferTimeoutMs are mutually exclusive (pick hard-deny on timeout OR soft-defer on timeout)",
|
|
54709
|
-
path: ["permissionDeferTimeoutMs"]
|
|
54710
|
-
};
|
|
54711
|
-
function timeoutsAreExclusive(data) {
|
|
54712
|
-
return !(data.permissionTimeoutMs !== undefined && data.permissionDeferTimeoutMs !== undefined);
|
|
54713
|
-
}
|
|
54714
|
-
var NewSessionRequestSchema = object({
|
|
54715
|
-
cwd: AbsolutePathSchema,
|
|
54716
|
-
mcpServers: array(McpServerSchema),
|
|
54717
|
-
...SessionOptionFields
|
|
54718
|
-
}).refine(metaCarriesNoCanonicalFields, { message: META_SMUGGLE_MESSAGE, path: ["_meta"] }).refine(timeoutsAreExclusive, TIMEOUT_EXCLUSION);
|
|
54719
|
-
var PromptRequestSchema = object({
|
|
54720
|
-
sessionId: string2().min(1),
|
|
54721
|
-
prompt: array(unknown()),
|
|
54722
|
-
model: string2().optional(),
|
|
54723
|
-
_meta: MetaSchema.optional()
|
|
54724
|
-
}).refine(metaCarriesNoCanonicalFields, { message: META_SMUGGLE_MESSAGE, path: ["_meta"] });
|
|
54725
|
-
var CancelRequestSchema = object({
|
|
54726
|
-
sessionId: string2().min(1)
|
|
54727
|
-
});
|
|
54728
|
-
var LoadSessionRequestSchema = object({
|
|
54729
|
-
sessionId: string2().min(1),
|
|
54730
|
-
cwd: AbsolutePathSchema,
|
|
54731
|
-
mcpServers: array(McpServerSchema),
|
|
54732
|
-
...SessionOptionFields
|
|
54733
|
-
}).refine(metaCarriesNoCanonicalFields, { message: META_SMUGGLE_MESSAGE, path: ["_meta"] }).refine(timeoutsAreExclusive, TIMEOUT_EXCLUSION);
|
|
54734
|
-
var CompactSessionRequestSchema = object({
|
|
54735
|
-
sessionId: string2().min(1)
|
|
54736
|
-
});
|
|
54737
|
-
var ForkSessionRequestSchema = object({
|
|
54738
|
-
sourceSessionId: string2().min(1),
|
|
54739
|
-
cwd: AbsolutePathSchema,
|
|
54740
|
-
mcpServers: array(McpServerSchema),
|
|
54741
|
-
systemPrompt: SystemPromptSchema.optional(),
|
|
54742
|
-
model: string2().optional(),
|
|
54743
|
-
additionalDirectories: array(AbsolutePathSchema).optional(),
|
|
54744
|
-
settingSources: SettingSourcesSchema.optional(),
|
|
54745
|
-
features: FeaturesSchema.optional(),
|
|
54746
|
-
_meta: MetaSchema.optional()
|
|
54747
|
-
}).refine(metaCarriesNoCanonicalFields, { message: META_SMUGGLE_MESSAGE, path: ["_meta"] });
|
|
54748
|
-
|
|
54749
55526
|
// src/server/acp-server.ts
|
|
55527
|
+
init_schemas3();
|
|
54750
55528
|
init_errors();
|
|
54751
55529
|
var JsonRpcErrorCode2 = {
|
|
54752
55530
|
ParseError: -32700,
|