@4pm/cli 1.5.22 → 1.5.25
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 +75 -26
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33013,6 +33013,15 @@ var dispatchCommandRequestSchema = external_exports.object({
|
|
|
33013
33013
|
* agent run (the Console tab + Git merge, which legitimately use tools).
|
|
33014
33014
|
*/
|
|
33015
33015
|
aiOneShot: external_exports.boolean().optional(),
|
|
33016
|
+
/**
|
|
33017
|
+
* Read-only agent AI mode (ADR-0265): the prompt is a task that must **read + inspect the repo
|
|
33018
|
+
* but write nothing** (the project-template "Analyze impact"). Unlike `aiOneShot` it keeps the
|
|
33019
|
+
* read/search tools (`Read`/`Glob`/`Grep`/`Bash`) and runs multi-turn, but the cli blocks the
|
|
33020
|
+
* write/orchestration tools and runs claude under `--permission-mode plan` (codex `--sandbox
|
|
33021
|
+
* read-only`) so it can diff files without editing them. Only meaningful with `ai:true`;
|
|
33022
|
+
* **mutually exclusive with `aiOneShot`** (a run is one-shot | read-only | full-agent).
|
|
33023
|
+
*/
|
|
33024
|
+
aiReadOnly: external_exports.boolean().optional(),
|
|
33016
33025
|
/**
|
|
33017
33026
|
* Per-run AI execution overrides (ADR-0261): model/thinking/temperature chosen in the web
|
|
33018
33027
|
* "AI settings" modal (per-user localStorage), layered over the profile default by the cli.
|
|
@@ -33027,6 +33036,13 @@ var dispatchCommandRequestSchema = external_exports.object({
|
|
|
33027
33036
|
message: "Provide exactly one of machineLinkId or pick."
|
|
33028
33037
|
});
|
|
33029
33038
|
}
|
|
33039
|
+
if (v.aiOneShot && v.aiReadOnly) {
|
|
33040
|
+
ctx.addIssue({
|
|
33041
|
+
code: external_exports.ZodIssueCode.custom,
|
|
33042
|
+
path: ["aiReadOnly"],
|
|
33043
|
+
message: "aiOneShot and aiReadOnly are mutually exclusive."
|
|
33044
|
+
});
|
|
33045
|
+
}
|
|
33030
33046
|
if (!v.ai && v.command.length > COMMAND_MAX_LEN) {
|
|
33031
33047
|
ctx.addIssue({
|
|
33032
33048
|
code: external_exports.ZodIssueCode.too_big,
|
|
@@ -33318,16 +33334,6 @@ var MEMO_MAX_LENGTH = 65536;
|
|
|
33318
33334
|
var putMemoRequestSchema = external_exports.object({
|
|
33319
33335
|
content: external_exports.string().max(MEMO_MAX_LENGTH)
|
|
33320
33336
|
});
|
|
33321
|
-
var MEMO_ITEM_MAX_LENGTH = 2e3;
|
|
33322
|
-
var createMemoItemSchema = external_exports.object({
|
|
33323
|
-
content: external_exports.string().trim().min(1).max(MEMO_ITEM_MAX_LENGTH)
|
|
33324
|
-
});
|
|
33325
|
-
var updateMemoItemSchema = external_exports.object({
|
|
33326
|
-
content: external_exports.string().trim().min(1).max(MEMO_ITEM_MAX_LENGTH).optional(),
|
|
33327
|
-
done: external_exports.boolean().optional()
|
|
33328
|
-
}).refine((v) => v.content !== void 0 || v.done !== void 0, {
|
|
33329
|
-
message: "content or done required"
|
|
33330
|
-
});
|
|
33331
33337
|
var HTTP_JSON_BODY_LIMIT_BYTES = 2 * 1048576;
|
|
33332
33338
|
var putSpecDraftRequestSchema = external_exports.object({
|
|
33333
33339
|
spec: external_exports.record(external_exports.unknown()),
|
|
@@ -34843,6 +34849,48 @@ function oneShotArgs(cmd) {
|
|
|
34843
34849
|
if (!cmd.includes("claude")) return [];
|
|
34844
34850
|
return ["--max-turns=1", "--disallowedTools", ONE_SHOT_DISALLOWED_CLAUDE_TOOLS, "--permission-mode", "default"];
|
|
34845
34851
|
}
|
|
34852
|
+
var READ_ONLY_DISALLOWED_CLAUDE_TOOLS = [
|
|
34853
|
+
"Edit",
|
|
34854
|
+
"Write",
|
|
34855
|
+
"NotebookEdit",
|
|
34856
|
+
"Task",
|
|
34857
|
+
"SlashCommand",
|
|
34858
|
+
"Skill",
|
|
34859
|
+
"WebSearch",
|
|
34860
|
+
"WebFetch",
|
|
34861
|
+
"TaskCreate",
|
|
34862
|
+
"TaskGet",
|
|
34863
|
+
"TaskList",
|
|
34864
|
+
"TaskOutput",
|
|
34865
|
+
"TaskStop",
|
|
34866
|
+
"TaskUpdate",
|
|
34867
|
+
"Monitor",
|
|
34868
|
+
"DesignSync",
|
|
34869
|
+
"CronCreate",
|
|
34870
|
+
"CronDelete",
|
|
34871
|
+
"CronList",
|
|
34872
|
+
"EnterWorktree",
|
|
34873
|
+
"ExitWorktree",
|
|
34874
|
+
"RemoteTrigger",
|
|
34875
|
+
"ScheduleWakeup",
|
|
34876
|
+
"SendMessage",
|
|
34877
|
+
"PushNotification"
|
|
34878
|
+
].join(",");
|
|
34879
|
+
var READ_ONLY_MAX_TURNS = 40;
|
|
34880
|
+
function readOnlyArgs(cmd, profileMaxTurns) {
|
|
34881
|
+
if (cmd.includes("claude")) {
|
|
34882
|
+
const turns = typeof profileMaxTurns === "number" && profileMaxTurns > 0 ? Math.floor(profileMaxTurns) : READ_ONLY_MAX_TURNS;
|
|
34883
|
+
return [
|
|
34884
|
+
`--max-turns=${turns}`,
|
|
34885
|
+
"--disallowedTools",
|
|
34886
|
+
READ_ONLY_DISALLOWED_CLAUDE_TOOLS,
|
|
34887
|
+
"--permission-mode",
|
|
34888
|
+
"plan"
|
|
34889
|
+
];
|
|
34890
|
+
}
|
|
34891
|
+
if (cmd.includes("codex")) return ["--sandbox", "read-only"];
|
|
34892
|
+
return [];
|
|
34893
|
+
}
|
|
34846
34894
|
function requiredArgs(cmd) {
|
|
34847
34895
|
if (cmd.includes("claude")) return REQUIRED_AI_ARGS.claude;
|
|
34848
34896
|
if (cmd.includes("codex")) return REQUIRED_AI_ARGS.codex;
|
|
@@ -34916,21 +34964,21 @@ function overrideEnv(cmd, o) {
|
|
|
34916
34964
|
const budget = THINKING_TOKENS[o.thinking];
|
|
34917
34965
|
return budget ? { MAX_THINKING_TOKENS: String(budget) } : {};
|
|
34918
34966
|
}
|
|
34919
|
-
function buildRunArgs(profile, cmd, resumeId, oneShot = false, override) {
|
|
34967
|
+
function buildRunArgs(profile, cmd, resumeId, oneShot = false, override, readOnly = false) {
|
|
34920
34968
|
const extras = profile.args ?? [];
|
|
34921
34969
|
const model = override?.model?.trim() || profile.model?.trim();
|
|
34922
34970
|
const modelArgs = model ? ["--model", model] : [];
|
|
34923
34971
|
const resumeArgs = resumeId && cmd.includes("claude") ? ["--resume", resumeId] : [];
|
|
34924
34972
|
const turns = profile.maxTurns;
|
|
34925
|
-
const perProfileTurns = !oneShot && cmd.includes("claude") && typeof turns === "number" && turns > 0 ? [`--max-turns=${Math.floor(turns)}`] : [];
|
|
34926
|
-
const
|
|
34973
|
+
const perProfileTurns = !oneShot && !readOnly && cmd.includes("claude") && typeof turns === "number" && turns > 0 ? [`--max-turns=${Math.floor(turns)}`] : [];
|
|
34974
|
+
const modeExtra = oneShot ? oneShotArgs(cmd) : readOnly ? readOnlyArgs(cmd, turns) : perProfileTurns;
|
|
34927
34975
|
return [
|
|
34928
34976
|
...requiredArgs(cmd),
|
|
34929
34977
|
...resumeArgs,
|
|
34930
34978
|
...extras,
|
|
34931
34979
|
...modelArgs,
|
|
34932
34980
|
...overrideArgs(cmd, override),
|
|
34933
|
-
...
|
|
34981
|
+
...modeExtra
|
|
34934
34982
|
];
|
|
34935
34983
|
}
|
|
34936
34984
|
function claudeHomeDirs(config) {
|
|
@@ -34970,10 +35018,10 @@ function resolveClaudeProfiles(config, workingDir) {
|
|
|
34970
35018
|
if (idx > 0) profiles.unshift(profiles.splice(idx, 1)[0]);
|
|
34971
35019
|
return profiles;
|
|
34972
35020
|
}
|
|
34973
|
-
function planAiRun(prompt, config, hint = {}, resume = /* @__PURE__ */ new Map(), oneShot = false, override) {
|
|
34974
|
-
return isUnifiedConfig(config) ? planUnifiedRun(prompt, config, hint.credential ?? null, resume, oneShot, override) : planLegacyRun(prompt, config, hint.dir ?? null, resume, oneShot, override);
|
|
35021
|
+
function planAiRun(prompt, config, hint = {}, resume = /* @__PURE__ */ new Map(), oneShot = false, override, readOnly = false) {
|
|
35022
|
+
return isUnifiedConfig(config) ? planUnifiedRun(prompt, config, hint.credential ?? null, resume, oneShot, override, readOnly) : planLegacyRun(prompt, config, hint.dir ?? null, resume, oneShot, override, readOnly);
|
|
34975
35023
|
}
|
|
34976
|
-
function planUnifiedRun(prompt, config, workingCredential, resume, oneShot, override) {
|
|
35024
|
+
function planUnifiedRun(prompt, config, workingCredential, resume, oneShot, override, readOnly = false) {
|
|
34977
35025
|
const baseEnv = config.aiEnv ?? {};
|
|
34978
35026
|
const active = activeProvider(config);
|
|
34979
35027
|
const usable = config.aiProfiles.filter(isUsableCredential);
|
|
@@ -34982,7 +35030,7 @@ function planUnifiedRun(prompt, config, workingCredential, resume, oneShot, over
|
|
|
34982
35030
|
return {
|
|
34983
35031
|
cmd: active,
|
|
34984
35032
|
attempts: [
|
|
34985
|
-
{ cmd: active, label: "default", dir: null, key: null, args: buildRunArgs({ profile: "" }, active, void 0, oneShot, override), stdin: prompt, env: { ...baseEnv, ...overrideEnv(active, override) } }
|
|
35033
|
+
{ cmd: active, label: "default", dir: null, key: null, args: buildRunArgs({ profile: "" }, active, void 0, oneShot, override, readOnly), stdin: prompt, env: { ...baseEnv, ...overrideEnv(active, override) } }
|
|
34986
35034
|
]
|
|
34987
35035
|
};
|
|
34988
35036
|
}
|
|
@@ -35000,20 +35048,20 @@ function planUnifiedRun(prompt, config, workingCredential, resume, oneShot, over
|
|
|
35000
35048
|
label: cred.label?.trim() || profileDisplayLabel(dir2),
|
|
35001
35049
|
dir: dir2,
|
|
35002
35050
|
key,
|
|
35003
|
-
args: buildRunArgs(cred, cmd, resume.get(key), oneShot, override),
|
|
35051
|
+
args: buildRunArgs(cred, cmd, resume.get(key), oneShot, override, readOnly),
|
|
35004
35052
|
stdin: prompt,
|
|
35005
35053
|
env: envVar ? { ...baseEnv, [envVar]: dir2, ...overrideEnv(cmd, override) } : { ...baseEnv, ...overrideEnv(cmd, override) }
|
|
35006
35054
|
}));
|
|
35007
35055
|
return { cmd: attempts[0]?.cmd ?? DEFAULT_AI_CLI, attempts };
|
|
35008
35056
|
}
|
|
35009
|
-
function planLegacyRun(prompt, config, workingDir, resume, oneShot, override) {
|
|
35057
|
+
function planLegacyRun(prompt, config, workingDir, resume, oneShot, override, readOnly = false) {
|
|
35010
35058
|
const cmd = config.aiCli || DEFAULT_AI_CLI;
|
|
35011
35059
|
const baseEnv = config.aiEnv ?? {};
|
|
35012
35060
|
const envVar = profileEnvVar(cmd);
|
|
35013
35061
|
const defaultPlan = {
|
|
35014
35062
|
cmd,
|
|
35015
35063
|
attempts: [
|
|
35016
|
-
{ cmd, label: "default", dir: null, key: null, args: buildRunArgs({ profile: "" }, cmd, void 0, oneShot, override), stdin: prompt, env: { ...baseEnv, ...overrideEnv(cmd, override) } }
|
|
35064
|
+
{ cmd, label: "default", dir: null, key: null, args: buildRunArgs({ profile: "" }, cmd, void 0, oneShot, override, readOnly), stdin: prompt, env: { ...baseEnv, ...overrideEnv(cmd, override) } }
|
|
35017
35065
|
]
|
|
35018
35066
|
};
|
|
35019
35067
|
if (!envVar) return defaultPlan;
|
|
@@ -35030,7 +35078,7 @@ function planLegacyRun(prompt, config, workingDir, resume, oneShot, override) {
|
|
|
35030
35078
|
label: profileDisplayLabel(dir2),
|
|
35031
35079
|
dir: dir2,
|
|
35032
35080
|
key,
|
|
35033
|
-
args: buildRunArgs(profile, cmd, resume.get(key), oneShot, override),
|
|
35081
|
+
args: buildRunArgs(profile, cmd, resume.get(key), oneShot, override, readOnly),
|
|
35034
35082
|
stdin: prompt,
|
|
35035
35083
|
env: { ...baseEnv, [envVar]: dir2, ...overrideEnv(cmd, override) }
|
|
35036
35084
|
};
|
|
@@ -38443,7 +38491,8 @@ var WsClient = class {
|
|
|
38443
38491
|
"server",
|
|
38444
38492
|
dispatch.aiOneShot ?? false,
|
|
38445
38493
|
dispatch.images,
|
|
38446
|
-
dispatch.aiConfig
|
|
38494
|
+
dispatch.aiConfig,
|
|
38495
|
+
dispatch.aiReadOnly ?? false
|
|
38447
38496
|
);
|
|
38448
38497
|
}
|
|
38449
38498
|
break;
|
|
@@ -38940,7 +38989,7 @@ var WsClient = class {
|
|
|
38940
38989
|
* exactly like typing the prompt in the cli (tries profiles, meters real tokens) instead
|
|
38941
38990
|
* of spawning the text as a raw command. Streams output to the transcript AND the server.
|
|
38942
38991
|
*/
|
|
38943
|
-
async runAiPrompt(prompt, commandId, origin, oneShot = false, images, aiConfig) {
|
|
38992
|
+
async runAiPrompt(prompt, commandId, origin, oneShot = false, images, aiConfig, readOnly = false) {
|
|
38944
38993
|
const config = readProfileConfig(this.context.profileDir);
|
|
38945
38994
|
const projectTimeoutSec = config.projectAiRunTimeoutSec ?? 0;
|
|
38946
38995
|
const machineTimeoutSec = config.aiRunTimeoutSec ?? 0;
|
|
@@ -39012,7 +39061,7 @@ var WsClient = class {
|
|
|
39012
39061
|
const guardedPrompt = this.restrictToFolder && this.physicRoot ? folderScopeGuard(this.physicRoot, aiBodyPrompt) : aiBodyPrompt;
|
|
39013
39062
|
const hint = unified ? { credential: startCred ?? workingCred } : { dir: startDir ?? workingDir };
|
|
39014
39063
|
const memCfg = resolveMemoryConfig(config);
|
|
39015
|
-
const firstAttempt = planAiRun(guardedPrompt, config, hint, /* @__PURE__ */ new Map(), oneShot, aiConfig).attempts[0];
|
|
39064
|
+
const firstAttempt = planAiRun(guardedPrompt, config, hint, /* @__PURE__ */ new Map(), oneShot, aiConfig, readOnly).attempts[0];
|
|
39016
39065
|
const resumeId = memCfg.enabled && firstAttempt?.key && firstAttempt.cmd === "claude" ? this.sessionIdByKey.get(firstAttempt.key) : void 0;
|
|
39017
39066
|
let effectivePrompt = guardedPrompt;
|
|
39018
39067
|
let resume = /* @__PURE__ */ new Map();
|
|
@@ -39024,7 +39073,7 @@ ${this.aiMemory}
|
|
|
39024
39073
|
|
|
39025
39074
|
${guardedPrompt}`;
|
|
39026
39075
|
}
|
|
39027
|
-
const plan = planAiRun(effectivePrompt, config, hint, resume, oneShot, aiConfig);
|
|
39076
|
+
const plan = planAiRun(effectivePrompt, config, hint, resume, oneShot, aiConfig, readOnly);
|
|
39028
39077
|
const markerArgs = plan.attempts[0]?.args ?? [];
|
|
39029
39078
|
const startStamp = formatTimestampInZone(/* @__PURE__ */ new Date(), this.orgTimezone);
|
|
39030
39079
|
this.bus.push({ source: origin, kind: "aireq", text: `${startStamp} ${plan.cmd} \u2039 ${prompt}` });
|