feishu-devops 26.627.1 → 26.627.2

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.
Files changed (2) hide show
  1. package/dist/cli.js +413 -23
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import { Command } from "commander";
4
4
  // package.json
5
5
  var package_default = {
6
6
  name: "feishu-devops",
7
- version: "26.627.1",
7
+ version: "26.627.2",
8
8
  description: "\u98DE\u4E66\u6D88\u606F bot\uFF1A\u626B\u7801\u521B\u5EFA\u5E94\u7528\uFF0C\u901A\u8FC7 Claude Code / Codex \u5904\u7406\u6D88\u606F",
9
9
  type: "module",
10
10
  bin: {
@@ -1926,7 +1926,7 @@ function getAgentPreflightDiagnostic(err) {
1926
1926
  function isAgentPreflightDiagnostic(input) {
1927
1927
  if (!input || typeof input !== "object") return false;
1928
1928
  const raw = input;
1929
- return typeof raw.code === "string" && raw.code.startsWith("agent-") && (raw.agentId === "claude" || raw.agentId === "codex" || raw.agentId === "cursor") && typeof raw.agentName === "string" && typeof raw.command === "string";
1929
+ return typeof raw.code === "string" && raw.code.startsWith("agent-") && (raw.agentId === "claude" || raw.agentId === "codex" || raw.agentId === "cursor" || raw.agentId === "pi") && typeof raw.agentName === "string" && typeof raw.command === "string";
1930
1930
  }
1931
1931
  function codeForSpawnError(err) {
1932
1932
  if (err.code === "ENOENT") return "agent-binary-not-found";
@@ -2987,6 +2987,354 @@ function isWindowsCommandNotFoundLine3(line) {
2987
2987
  return process.platform === "win32" && /is not recognized as an internal or external command|operable program or batch file/i.test(line);
2988
2988
  }
2989
2989
 
2990
+ // src/agent/pi/adapter.ts
2991
+ import { createInterface as createInterface5 } from "readline";
2992
+
2993
+ // src/agent/pi/stream-json.ts
2994
+ function createPiStreamState() {
2995
+ return {
2996
+ sawPartialAssistantText: false,
2997
+ emittedToolCalls: /* @__PURE__ */ new Set(),
2998
+ doneEmitted: false
2999
+ };
3000
+ }
3001
+ function* translateEvent3(raw, state) {
3002
+ if (!raw || typeof raw !== "object") return;
3003
+ const evt = raw;
3004
+ if (evt.type === "session") {
3005
+ const header = evt;
3006
+ state.sessionId = header.id;
3007
+ state.cwd = header.cwd;
3008
+ yield {
3009
+ type: "system",
3010
+ sessionId: header.id,
3011
+ cwd: header.cwd
3012
+ };
3013
+ return;
3014
+ }
3015
+ if (evt.type === "message_update" && evt.assistantMessageEvent) {
3016
+ const assistantEvt = evt.assistantMessageEvent;
3017
+ if (assistantEvt.type === "text_delta" && typeof assistantEvt.delta === "string" && assistantEvt.delta) {
3018
+ state.sawPartialAssistantText = true;
3019
+ yield { type: "text", delta: assistantEvt.delta };
3020
+ return;
3021
+ }
3022
+ if (assistantEvt.type === "thinking_delta" && typeof assistantEvt.delta === "string" && assistantEvt.delta) {
3023
+ yield { type: "thinking", delta: assistantEvt.delta };
3024
+ return;
3025
+ }
3026
+ if (assistantEvt.type === "toolcall_end" && assistantEvt.toolCall) {
3027
+ yield* emitToolUse(
3028
+ assistantEvt.toolCall.id,
3029
+ assistantEvt.toolCall.name,
3030
+ assistantEvt.toolCall.arguments,
3031
+ state
3032
+ );
3033
+ }
3034
+ return;
3035
+ }
3036
+ if (evt.type === "tool_execution_start" && evt.toolCallId && evt.toolName) {
3037
+ yield* emitToolUse(evt.toolCallId, evt.toolName, evt.args, state);
3038
+ return;
3039
+ }
3040
+ if (evt.type === "tool_execution_end" && evt.toolCallId) {
3041
+ const output = typeof evt.result === "string" ? evt.result : JSON.stringify(evt.result ?? "");
3042
+ yield {
3043
+ type: "tool_result",
3044
+ id: evt.toolCallId,
3045
+ output,
3046
+ isError: evt.isError === true
3047
+ };
3048
+ return;
3049
+ }
3050
+ if (evt.type === "message_end" && evt.message?.role === "assistant") {
3051
+ const message = evt.message;
3052
+ if (message.stopReason === "error" || message.stopReason === "aborted") {
3053
+ yield {
3054
+ type: "error",
3055
+ message: message.errorMessage || `pi agent run ${message.stopReason}`,
3056
+ terminationReason: message.stopReason === "aborted" ? "interrupted" : "failed"
3057
+ };
3058
+ }
3059
+ return;
3060
+ }
3061
+ if (evt.type === "turn_end" && evt.message?.role === "assistant") {
3062
+ yield* emitUsage(evt.message.usage);
3063
+ return;
3064
+ }
3065
+ if (evt.type === "agent_end") {
3066
+ const lastAssistant = [...evt.messages ?? []].reverse().find((message) => message.role === "assistant");
3067
+ if (lastAssistant?.usage) {
3068
+ yield* emitUsage(lastAssistant.usage);
3069
+ }
3070
+ if (lastAssistant?.stopReason === "error" || lastAssistant?.stopReason === "aborted") {
3071
+ yield {
3072
+ type: "error",
3073
+ message: lastAssistant.errorMessage || `pi agent run ${lastAssistant.stopReason}`,
3074
+ terminationReason: lastAssistant.stopReason === "aborted" ? "interrupted" : "failed"
3075
+ };
3076
+ return;
3077
+ }
3078
+ if (!state.doneEmitted) {
3079
+ state.doneEmitted = true;
3080
+ yield {
3081
+ type: "done",
3082
+ sessionId: state.sessionId,
3083
+ terminationReason: "normal"
3084
+ };
3085
+ }
3086
+ }
3087
+ }
3088
+ function* emitToolUse(id, name, input, state) {
3089
+ if (!id || !name || state.emittedToolCalls.has(id)) return;
3090
+ state.emittedToolCalls.add(id);
3091
+ yield { type: "tool_use", id, name, input };
3092
+ }
3093
+ function* emitUsage(usage) {
3094
+ if (!usage) return;
3095
+ yield {
3096
+ type: "usage",
3097
+ inputTokens: usage.input,
3098
+ outputTokens: usage.output,
3099
+ cachedInputTokens: usage.cacheRead,
3100
+ costUsd: usage.cost?.total
3101
+ };
3102
+ }
3103
+
3104
+ // src/agent/pi/adapter.ts
3105
+ function isPiDebugEnabled(explicit) {
3106
+ if (explicit === true) return true;
3107
+ if (explicit === false) return false;
3108
+ const raw = process.env.FEISHU_DEVOPS_PI_DEBUG ?? process.env.LARK_CHANNEL_PI_DEBUG;
3109
+ return raw === "1" || raw === "true";
3110
+ }
3111
+ var PiAdapter = class {
3112
+ id = "pi";
3113
+ displayName = "Pi Agent";
3114
+ binary;
3115
+ larkChannel;
3116
+ debug;
3117
+ botIdentity;
3118
+ constructor(opts = {}) {
3119
+ this.binary = opts.binary ?? "pi";
3120
+ this.larkChannel = opts.larkChannel;
3121
+ this.debug = isPiDebugEnabled(opts.debug);
3122
+ }
3123
+ setBotIdentity(identity) {
3124
+ this.botIdentity = identity;
3125
+ }
3126
+ async isAvailable() {
3127
+ return (await this.checkAvailability()).ok;
3128
+ }
3129
+ async checkAvailability() {
3130
+ return checkAgentAvailability({
3131
+ agentId: "pi",
3132
+ agentName: "Pi Agent",
3133
+ command: this.binary,
3134
+ binaryPath: this.binary,
3135
+ args: ["--version"]
3136
+ });
3137
+ }
3138
+ run(opts) {
3139
+ if (!opts.cwd) {
3140
+ throw new Error("cwd is required for PiAdapter.run");
3141
+ }
3142
+ const systemPrompt = buildBridgeSystemPrompt(this.botIdentity);
3143
+ const args = ["--mode", "json", "-p"];
3144
+ if (systemPrompt) {
3145
+ args.push("--append-system-prompt", systemPrompt);
3146
+ }
3147
+ if (opts.sessionId) args.push("--session-id", opts.sessionId);
3148
+ if (opts.model) args.push("--model", opts.model);
3149
+ args.push(opts.prompt);
3150
+ if (this.debug) {
3151
+ console.log("[pi debug] spawn", {
3152
+ binary: this.binary,
3153
+ cwd: opts.cwd,
3154
+ args: formatDebugArgs2(args)
3155
+ });
3156
+ }
3157
+ const child = spawnProcess(this.binary, args, {
3158
+ cwd: opts.cwd,
3159
+ env: mergeProcessEnv(process.env, buildLarkChannelEnv(this.larkChannel)),
3160
+ stdio: ["ignore", "pipe", "pipe"]
3161
+ });
3162
+ log.info("agent", "spawn", {
3163
+ pid: child.pid ?? null,
3164
+ cwd: opts.cwd ?? process.cwd(),
3165
+ hasSession: Boolean(opts.sessionId),
3166
+ promptChars: opts.prompt.length,
3167
+ model: opts.model,
3168
+ agent: "pi"
3169
+ });
3170
+ const stderrChunks = [];
3171
+ let runtimeError = null;
3172
+ let stderrBuffer = "";
3173
+ child.stderr.on("data", (chunk) => {
3174
+ stderrChunks.push(chunk);
3175
+ stderrBuffer += chunk.toString("utf8");
3176
+ let nl = stderrBuffer.indexOf("\n");
3177
+ while (nl !== -1) {
3178
+ const line = stderrBuffer.slice(0, nl);
3179
+ stderrBuffer = stderrBuffer.slice(nl + 1);
3180
+ if (line.trim()) log.warn("agent", "stderr", { line, agent: "pi" });
3181
+ if (isWindowsCommandNotFoundLine4(line)) {
3182
+ runtimeError = new Error(`failed to spawn pi: ${line.trim()}`);
3183
+ child.stdout.destroy();
3184
+ child.kill();
3185
+ }
3186
+ nl = stderrBuffer.indexOf("\n");
3187
+ }
3188
+ });
3189
+ child.on("error", (err) => {
3190
+ runtimeError = err;
3191
+ });
3192
+ child.on("exit", (code, signal) => {
3193
+ log.info("agent", "exit", { pid: child.pid ?? null, code, signal, agent: "pi" });
3194
+ });
3195
+ const stopGraceMs = opts.stopGraceMs ?? 5e3;
3196
+ return {
3197
+ runId: opts.runId,
3198
+ events: createEventStream4(child, stderrChunks, () => runtimeError, this.debug),
3199
+ async stop() {
3200
+ if (child.exitCode !== null || child.signalCode !== null) return;
3201
+ log.info("agent", "stop-sigterm", {
3202
+ pid: child.pid ?? null,
3203
+ graceMs: stopGraceMs,
3204
+ agent: "pi"
3205
+ });
3206
+ child.kill("SIGTERM");
3207
+ await new Promise((resolve3) => {
3208
+ const timer = setTimeout(() => {
3209
+ if (child.exitCode === null && child.signalCode === null) {
3210
+ log.warn("agent", "stop-sigkill", {
3211
+ pid: child.pid ?? null,
3212
+ graceMs: stopGraceMs,
3213
+ reason: "grace-period-expired",
3214
+ agent: "pi"
3215
+ });
3216
+ child.kill("SIGKILL");
3217
+ }
3218
+ resolve3();
3219
+ }, stopGraceMs);
3220
+ child.once("exit", () => {
3221
+ clearTimeout(timer);
3222
+ resolve3();
3223
+ });
3224
+ });
3225
+ },
3226
+ waitForExit(timeoutMs) {
3227
+ if (child.exitCode !== null || child.signalCode !== null) {
3228
+ return Promise.resolve(true);
3229
+ }
3230
+ return new Promise((resolve3) => {
3231
+ const onExit = () => {
3232
+ clearTimeout(timer);
3233
+ resolve3(true);
3234
+ };
3235
+ const timer = setTimeout(() => {
3236
+ child.removeListener("exit", onExit);
3237
+ resolve3(false);
3238
+ }, timeoutMs);
3239
+ child.once("exit", onExit);
3240
+ });
3241
+ }
3242
+ };
3243
+ }
3244
+ };
3245
+ async function* createEventStream4(child, stderrChunks, getError, debug) {
3246
+ if (!child.pid) {
3247
+ const err = getError();
3248
+ yield {
3249
+ type: "error",
3250
+ message: err ? `failed to spawn pi: ${err.message}` : "spawn returned no pid",
3251
+ terminationReason: "failed"
3252
+ };
3253
+ return;
3254
+ }
3255
+ const streamState = createPiStreamState();
3256
+ const rl = createInterface5({ input: child.stdout, crlfDelay: Infinity });
3257
+ let sawStdout = false;
3258
+ let silentExitTimer;
3259
+ const closeSilentStdout = () => {
3260
+ silentExitTimer = setTimeout(() => {
3261
+ if (!sawStdout && !child.stdout.readableEnded) child.stdout.destroy();
3262
+ }, 50);
3263
+ };
3264
+ child.once("exit", closeSilentStdout);
3265
+ try {
3266
+ for await (const line of rl) {
3267
+ sawStdout = true;
3268
+ const trimmed = line.trim();
3269
+ if (!trimmed) continue;
3270
+ let parsed;
3271
+ try {
3272
+ parsed = JSON.parse(trimmed);
3273
+ } catch {
3274
+ continue;
3275
+ }
3276
+ if (debug) {
3277
+ console.log("[pi debug] event", parsed);
3278
+ }
3279
+ yield* translateEvent3(parsed, streamState);
3280
+ }
3281
+ } finally {
3282
+ if (silentExitTimer) clearTimeout(silentExitTimer);
3283
+ child.removeListener("exit", closeSilentStdout);
3284
+ rl.close();
3285
+ }
3286
+ const earlyRuntimeError = getError();
3287
+ if (earlyRuntimeError && child.exitCode === null && child.signalCode === null) {
3288
+ yield {
3289
+ type: "error",
3290
+ message: `pi runtime error: ${earlyRuntimeError.message}`,
3291
+ terminationReason: "failed"
3292
+ };
3293
+ return;
3294
+ }
3295
+ const exitCode = await new Promise((resolve3) => {
3296
+ if (child.exitCode !== null || child.signalCode !== null) {
3297
+ resolve3(child.exitCode);
3298
+ } else {
3299
+ child.once("exit", (code) => resolve3(code));
3300
+ }
3301
+ });
3302
+ const runtimeError = getError();
3303
+ if (!streamState.doneEmitted && exitCode === 0) {
3304
+ yield {
3305
+ type: "done",
3306
+ sessionId: streamState.sessionId,
3307
+ terminationReason: "normal"
3308
+ };
3309
+ } else if (exitCode !== 0 && exitCode !== null) {
3310
+ const stderr = Buffer.concat(stderrChunks).toString("utf8").trim();
3311
+ const detail = stderr ? `: ${stderr.slice(0, 500)}` : "";
3312
+ yield {
3313
+ type: "error",
3314
+ message: `pi agent exited with code ${exitCode}${detail}`,
3315
+ terminationReason: "failed"
3316
+ };
3317
+ } else if (runtimeError) {
3318
+ yield {
3319
+ type: "error",
3320
+ message: `pi runtime error: ${runtimeError.message}`,
3321
+ terminationReason: "failed"
3322
+ };
3323
+ }
3324
+ }
3325
+ function formatDebugArgs2(args) {
3326
+ const promptIndex = args.length - 1;
3327
+ if (promptIndex < 0) return args;
3328
+ const prompt = args[promptIndex];
3329
+ if (typeof prompt !== "string" || prompt.length <= 200) return args;
3330
+ const copy = [...args];
3331
+ copy[promptIndex] = `${prompt.slice(0, 200)}\u2026 (${prompt.length} chars)`;
3332
+ return copy;
3333
+ }
3334
+ function isWindowsCommandNotFoundLine4(line) {
3335
+ return process.platform === "win32" && /is not recognized as an internal or external command|operable program or batch file/i.test(line);
3336
+ }
3337
+
2990
3338
  // src/config/permissions.ts
2991
3339
  var ACCESS_ORDER = {
2992
3340
  "read-only": 0,
@@ -3188,8 +3536,8 @@ function normalizeProfileConfig(input) {
3188
3536
  if (raw.schemaVersion !== 2) {
3189
3537
  throw new Error("profile schemaVersion must be 2");
3190
3538
  }
3191
- if (raw.agentKind !== "claude" && raw.agentKind !== "codex" && raw.agentKind !== "cursor") {
3192
- throw new Error("agentKind must be claude, codex, or cursor");
3539
+ if (raw.agentKind !== "claude" && raw.agentKind !== "codex" && raw.agentKind !== "cursor" && raw.agentKind !== "pi") {
3540
+ throw new Error("agentKind must be claude, codex, cursor, or pi");
3193
3541
  }
3194
3542
  const accounts = normalizeAccounts(raw.accounts);
3195
3543
  if (raw.agentKind === "codex" && !raw.codex) {
@@ -3333,7 +3681,7 @@ function numberOr(value, fallback) {
3333
3681
 
3334
3682
  // src/config/agent-config.ts
3335
3683
  function toProfileConfig(cfg) {
3336
- const agentKind = cfg.agentKind === "codex" ? "codex" : cfg.agentKind === "cursor" ? "cursor" : "claude";
3684
+ const agentKind = cfg.agentKind === "codex" ? "codex" : cfg.agentKind === "cursor" ? "cursor" : cfg.agentKind === "pi" ? "pi" : "claude";
3337
3685
  const base = {
3338
3686
  schemaVersion: 2,
3339
3687
  agentKind,
@@ -3426,6 +3774,14 @@ function createRuntimeAgent(cfg, opts = {}) {
3426
3774
  ...opts.cursorDebug === true ? { debug: true } : {}
3427
3775
  });
3428
3776
  }
3777
+ if (profileConfig.agentKind === "pi") {
3778
+ const command = process.env.FEISHU_DEVOPS_PI_BIN ?? process.env.LARK_CHANNEL_PI_BIN ?? "pi";
3779
+ return new PiAdapter({
3780
+ binary: command,
3781
+ larkChannel,
3782
+ ...opts.cursorDebug === true ? { debug: true } : {}
3783
+ });
3784
+ }
3429
3785
  return new ClaudeAdapter({ larkChannel });
3430
3786
  }
3431
3787
  async function checkRuntimeAgentAvailability(agent) {
@@ -3453,6 +3809,10 @@ async function resolveCursorBinaryPath() {
3453
3809
  const command = process.env.FEISHU_DEVOPS_CURSOR_BIN ?? process.env.LARK_CHANNEL_CURSOR_BIN ?? "agent";
3454
3810
  return resolveExecutablePath(command);
3455
3811
  }
3812
+ async function resolvePiBinaryPath() {
3813
+ const command = process.env.FEISHU_DEVOPS_PI_BIN ?? process.env.LARK_CHANNEL_PI_BIN ?? "pi";
3814
+ return resolveExecutablePath(command);
3815
+ }
3456
3816
  function applyAgentKindToConfig(cfg, agentKind, codexBinaryPath) {
3457
3817
  const next = { ...cfg, agentKind };
3458
3818
  if (agentKind === "codex" && codexBinaryPath) {
@@ -4006,7 +4366,7 @@ var SessionCatalog = class {
4006
4366
  function normalizeEntry(input) {
4007
4367
  if (!input || typeof input !== "object") return void 0;
4008
4368
  const raw = input;
4009
- if (typeof raw.key !== "string" || typeof raw.scopeId !== "string" || raw.agentId !== "claude" && raw.agentId !== "codex" && raw.agentId !== "cursor" || typeof raw.cwdRealpath !== "string" || typeof raw.policyFingerprint !== "string" || raw.status !== "active" && raw.status !== "archived" || typeof raw.updatedAt !== "number") {
4369
+ if (typeof raw.key !== "string" || typeof raw.scopeId !== "string" || raw.agentId !== "claude" && raw.agentId !== "codex" && raw.agentId !== "cursor" && raw.agentId !== "pi" || typeof raw.cwdRealpath !== "string" || typeof raw.policyFingerprint !== "string" || raw.status !== "active" && raw.status !== "archived" || typeof raw.updatedAt !== "number") {
4010
4370
  return void 0;
4011
4371
  }
4012
4372
  return {
@@ -4026,15 +4386,15 @@ function matchesIdentity(entry, input) {
4026
4386
  return entry.scopeId === input.scopeId && entry.agentId === input.agentId && entry.cwdRealpath === input.cwdRealpath && entry.policyFingerprint === input.policyFingerprint && entry.key === sessionCatalogKey(input);
4027
4387
  }
4028
4388
  function isValidAgentEntry(entry) {
4029
- if (entry.agentId === "claude" || entry.agentId === "cursor") {
4389
+ if (entry.agentId === "claude" || entry.agentId === "cursor" || entry.agentId === "pi") {
4030
4390
  return Boolean(entry.sessionId) && !entry.threadId;
4031
4391
  }
4032
4392
  return Boolean(entry.threadId) && !entry.sessionId;
4033
4393
  }
4034
4394
  function assertAgentIdentity(input) {
4035
- if (input.agentId === "claude" || input.agentId === "cursor") {
4395
+ if (input.agentId === "claude" || input.agentId === "cursor" || input.agentId === "pi") {
4036
4396
  if (!input.sessionId || input.threadId) {
4037
- throw new Error("Claude/Cursor catalog entries require sessionId and must not include threadId");
4397
+ throw new Error("Claude/Cursor/Pi catalog entries require sessionId and must not include threadId");
4038
4398
  }
4039
4399
  return;
4040
4400
  }
@@ -4231,6 +4591,23 @@ function cursorCapability(profile) {
4231
4591
  }
4232
4592
  };
4233
4593
  }
4594
+ function piCapability(profile) {
4595
+ const maxAccess = profile?.permissions.maxAccess ?? "full";
4596
+ return {
4597
+ agentId: "pi",
4598
+ sessionKind: "pi-session",
4599
+ promptInjection: "append-system-prompt",
4600
+ systemPrompt: BRIDGE_SYSTEM_PROMPT,
4601
+ supportsNativeHistory: true,
4602
+ callback: {
4603
+ marker: "__bridge_cb",
4604
+ legacyMarkers: []
4605
+ },
4606
+ permissions: {
4607
+ maxAccess
4608
+ }
4609
+ };
4610
+ }
4234
4611
  function codexCapability(profile) {
4235
4612
  const maxAccess = profile.permissions.maxAccess;
4236
4613
  return {
@@ -4893,9 +5270,6 @@ function classifyHighRiskWorkingDirectory(real, requestedCwd, tempRealpath) {
4893
5270
  return reject2("filesystem-root", requestedCwd, "\u4E0D\u80FD\u628A\u6587\u4EF6\u7CFB\u7EDF\u6839\u76EE\u5F55\u8BBE\u4E3A\u5DE5\u4F5C\u76EE\u5F55\u3002");
4894
5271
  }
4895
5272
  const home = resolve2(homedir4());
4896
- if (real === home) {
4897
- return reject2("home-root", requestedCwd, "\u4E0D\u80FD\u628A Home \u6839\u76EE\u5F55\u8BBE\u4E3A\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u8BF7\u9009\u62E9\u66F4\u5177\u4F53\u7684\u5B50\u76EE\u5F55\u3002");
4898
- }
4899
5273
  if (real === dirname7(home)) {
4900
5274
  return reject2("user-root", requestedCwd, "\u4E0D\u80FD\u628A\u7528\u6237\u76EE\u5F55\u6839\u8BBE\u4E3A\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u8BF7\u9009\u62E9\u66F4\u5177\u4F53\u7684\u5B50\u76EE\u5F55\u3002");
4901
5275
  }
@@ -4970,7 +5344,7 @@ async function startRunFlow(input) {
4970
5344
  cwdRealpath: workspace.cwdRealpath,
4971
5345
  policyFingerprint: policy.policyFingerprint
4972
5346
  });
4973
- if (catalogEntry?.agentId === "claude" || catalogEntry?.agentId === "cursor") {
5347
+ if (catalogEntry?.agentId === "claude" || catalogEntry?.agentId === "cursor" || catalogEntry?.agentId === "pi") {
4974
5348
  sessionId = catalogEntry.sessionId;
4975
5349
  resumeFrom = sessionId;
4976
5350
  } else if (catalogEntry?.agentId === "codex") {
@@ -4978,7 +5352,7 @@ async function startRunFlow(input) {
4978
5352
  resumeFrom = threadId;
4979
5353
  }
4980
5354
  }
4981
- if (!resumeFrom && (input.capability.agentId === "claude" || input.capability.agentId === "cursor")) {
5355
+ if (!resumeFrom && (input.capability.agentId === "claude" || input.capability.agentId === "cursor" || input.capability.agentId === "pi")) {
4982
5356
  resumeFrom = input.sessions.resumeFor(input.scopeId, workspace.cwdRealpath);
4983
5357
  sessionId = resumeFrom;
4984
5358
  const stale = input.sessions.getRaw(input.scopeId);
@@ -5020,7 +5394,7 @@ async function startRunFlow(input) {
5020
5394
  }
5021
5395
  function recordRunSessionEvent(input) {
5022
5396
  if (input.event.type !== "system") return;
5023
- if ((input.capability.agentId === "claude" || input.capability.agentId === "cursor") && input.event.sessionId) {
5397
+ if ((input.capability.agentId === "claude" || input.capability.agentId === "cursor" || input.capability.agentId === "pi") && input.event.sessionId) {
5024
5398
  const cwdRealpath = input.event.cwd ?? input.policy.cwdRealpath;
5025
5399
  input.sessions.set(input.scopeId, input.event.sessionId, cwdRealpath);
5026
5400
  input.sessionCatalog?.upsertActive({
@@ -5072,7 +5446,7 @@ async function runAgentMessage(deps, msg) {
5072
5446
  replyTo: msg.messageId,
5073
5447
  ...msg.threadId ? { replyInThread: true } : {}
5074
5448
  };
5075
- const capability = profileConfig.agentKind === "codex" ? codexCapability(profileConfig) : profileConfig.agentKind === "cursor" ? cursorCapability(profileConfig) : claudeCapability(profileConfig);
5449
+ const capability = profileConfig.agentKind === "codex" ? codexCapability(profileConfig) : profileConfig.agentKind === "cursor" ? cursorCapability(profileConfig) : profileConfig.agentKind === "pi" ? piCapability(profileConfig) : claudeCapability(profileConfig);
5076
5450
  const scopeContext = {
5077
5451
  source: "im",
5078
5452
  chatId: msg.chatId,
@@ -5439,6 +5813,9 @@ async function buildConfigForKind(cfg, kind) {
5439
5813
  if (kind === "cursor") {
5440
5814
  return applyAgentKindToConfig(cfg, "cursor");
5441
5815
  }
5816
+ if (kind === "pi") {
5817
+ return applyAgentKindToConfig(cfg, "pi");
5818
+ }
5442
5819
  return applyAgentKindToConfig(cfg, "claude");
5443
5820
  }
5444
5821
  function clearAgentSessionState(sessions, sessionCatalog) {
@@ -5458,6 +5835,7 @@ function clearAgentSessionState(sessions, sessionCatalog) {
5458
5835
  function agentLabel(kind) {
5459
5836
  if (kind === "codex") return "Codex CLI";
5460
5837
  if (kind === "cursor") return "Cursor Agent";
5838
+ if (kind === "pi") return "Pi Agent";
5461
5839
  return "Claude Code";
5462
5840
  }
5463
5841
 
@@ -5565,7 +5943,7 @@ function helpCard(agentName = "Agent") {
5565
5943
  "- `/cwd view` \u2014 \u67E5\u770B\u5DE5\u4F5C\u76EE\u5F55",
5566
5944
  "- `/status` \u2014 \u5F53\u524D\u72B6\u6001",
5567
5945
  "- `/stop` \u2014 \u7ED3\u675F\u5F53\u524D\u6B63\u5728\u8DD1\u7684\u4EFB\u52A1",
5568
- "- `/use claude|codex|cursor` \u2014 \u5207\u6362 agent\uFF08\u65E0\u9700\u91CD\u542F\uFF09",
5946
+ "- `/use claude|codex|cursor|pi` \u2014 \u5207\u6362 agent\uFF08\u65E0\u9700\u91CD\u542F\uFF09",
5569
5947
  "- `/cmd <shell>` / `$ <shell>` \u2014 \u672C\u673A\u6267\u884C shell",
5570
5948
  "- `/help` \u2014 \u672C\u5E2E\u52A9",
5571
5949
  "",
@@ -5990,7 +6368,7 @@ async function handleResume(args, ctx) {
5990
6368
  const entries = sessionId ? [
5991
6369
  {
5992
6370
  sessionId,
5993
- preview: isCodex ? "\u5F53\u524D Codex thread" : profile?.agentKind === "cursor" ? "\u5F53\u524D Cursor \u4F1A\u8BDD" : "\u5F53\u524D Claude \u4F1A\u8BDD",
6371
+ preview: isCodex ? "\u5F53\u524D Codex thread" : profile?.agentKind === "cursor" ? "\u5F53\u524D Cursor \u4F1A\u8BDD" : profile?.agentKind === "pi" ? "\u5F53\u524D Pi \u4F1A\u8BDD" : "\u5F53\u524D Claude \u4F1A\u8BDD",
5994
6372
  relTime: "\u5F53\u524D",
5995
6373
  current: true
5996
6374
  }
@@ -6018,7 +6396,7 @@ async function handleResumeUse(sessionId, ctx) {
6018
6396
  return;
6019
6397
  }
6020
6398
  ctx.sessions?.set(ctx.scope, sessionId, cwd);
6021
- const agentLabel2 = profile?.agentKind === "cursor" ? "Cursor" : "Claude";
6399
+ const agentLabel2 = profile?.agentKind === "cursor" ? "Cursor" : profile?.agentKind === "pi" ? "Pi" : "Claude";
6022
6400
  await replyMarkdown(ctx, `\u2713 \u5DF2\u6062\u590D ${agentLabel2} \u4F1A\u8BDD\uFF0C\u8BF7\u7EE7\u7EED\u53D1\u9001\u6D88\u606F\u3002`);
6023
6401
  }
6024
6402
  async function handleStop(_args, ctx) {
@@ -6034,7 +6412,7 @@ async function handleSwitch(args, ctx) {
6034
6412
  }
6035
6413
  const kind = args.trim().toLowerCase();
6036
6414
  if (!isSwitchableAgentKind(kind)) {
6037
- await replyText(ctx, "\u7528\u6CD5: `/use claude` | `/use codex` | `/use cursor`");
6415
+ await replyText(ctx, "\u7528\u6CD5: `/use claude` | `/use codex` | `/use cursor` | `/use pi`");
6038
6416
  return;
6039
6417
  }
6040
6418
  const current = ctx.runtime.fullCfg.agentKind ?? "claude";
@@ -6067,7 +6445,7 @@ async function handleSwitch(args, ctx) {
6067
6445
  );
6068
6446
  }
6069
6447
  function isSwitchableAgentKind(value) {
6070
- return value === "claude" || value === "codex" || value === "cursor";
6448
+ return value === "claude" || value === "codex" || value === "cursor" || value === "pi";
6071
6449
  }
6072
6450
  function resolveStatusAgentName(ctx) {
6073
6451
  if (ctx.runtime?.agentUnavailable) {
@@ -6090,6 +6468,9 @@ function runtimeAccessStatus(profile) {
6090
6468
  if (profile.agentKind === "cursor") {
6091
6469
  return { label: "mode", value: "force" };
6092
6470
  }
6471
+ if (profile.agentKind === "pi") {
6472
+ return { label: "mode", value: "json" };
6473
+ }
6093
6474
  return {
6094
6475
  label: "sandbox",
6095
6476
  value: `${profile.sandbox.defaultMode}/${profile.sandbox.maxMode}`
@@ -6457,6 +6838,8 @@ async function runStart(opts) {
6457
6838
  fullCfg = applyAgentKindToConfig(fullCfg, "codex", binaryPath);
6458
6839
  } else if (agentKind === "cursor") {
6459
6840
  fullCfg = applyAgentKindToConfig(fullCfg, "cursor");
6841
+ } else if (agentKind === "pi") {
6842
+ fullCfg = applyAgentKindToConfig(fullCfg, "pi");
6460
6843
  } else {
6461
6844
  fullCfg = applyAgentKindToConfig(fullCfg, "claude");
6462
6845
  }
@@ -6493,6 +6876,13 @@ async function runStart(opts) {
6493
6876
  } catch {
6494
6877
  console.log(`\u2713 Cursor Agent: agent${opts.debug ? " (debug)" : ""}`);
6495
6878
  }
6879
+ } else if (agentKind === "pi") {
6880
+ try {
6881
+ const piPath = await resolvePiBinaryPath();
6882
+ console.log(`\u2713 Pi Agent: ${piPath}${opts.debug ? " (debug)" : ""}`);
6883
+ } catch {
6884
+ console.log(`\u2713 Pi Agent: pi${opts.debug ? " (debug)" : ""}`);
6885
+ }
6496
6886
  } else {
6497
6887
  console.log(`\u2713 Codex CLI: ${fullCfg.codex?.binaryPath ?? "codex"}`);
6498
6888
  }
@@ -6543,11 +6933,11 @@ var runOptions = [
6543
6933
  ["--app-id <id>", "\u4F7F\u7528\u5DF2\u6709\u98DE\u4E66\u5E94\u7528\uFF08\u8DF3\u8FC7\u626B\u7801\u521B\u5EFA\uFF09"],
6544
6934
  ["--app-secret <secret>", "App Secret\uFF08\u914D\u5408 --app-id\uFF09"],
6545
6935
  ["--tenant <tenant>", "\u79DF\u6237\u57DF\u540D\uFF1Afeishu \u6216 lark\uFF08\u9ED8\u8BA4 feishu\uFF09"],
6546
- ["--agent <kind>", "agent \u7C7B\u578B\uFF1Aclaude / codex / cursor / disabled\uFF08\u9ED8\u8BA4 claude\uFF09"],
6936
+ ["--agent <kind>", "agent \u7C7B\u578B\uFF1Aclaude / codex / cursor / pi / disabled\uFF08\u9ED8\u8BA4 claude\uFF09"],
6547
6937
  ["--debug", "Cursor agent \u8C03\u8BD5\uFF1A\u6253\u5370 spawn args \u4E0E\u539F\u751F stream-json \u4E8B\u4EF6"]
6548
6938
  ];
6549
6939
  function parseRunOpts(opts) {
6550
- const agent = opts.agent === "claude" || opts.agent === "codex" || opts.agent === "cursor" || opts.agent === "disabled" ? opts.agent : void 0;
6940
+ const agent = opts.agent === "claude" || opts.agent === "codex" || opts.agent === "cursor" || opts.agent === "pi" || opts.agent === "disabled" ? opts.agent : void 0;
6551
6941
  return { ...opts, agent, debug: opts.debug === true };
6552
6942
  }
6553
6943
  program.command("run").description("\u524D\u53F0\u8FD0\u884C bot\uFF0C\u63A5\u6536\u6D88\u606F\u5E76\u901A\u8FC7 Claude Code / Codex / Cursor \u56DE\u590D").option(...runOptions[0]).option(...runOptions[1]).option(...runOptions[2]).option(...runOptions[3]).option(...runOptions[4]).option(...runOptions[5]).action(async (opts) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feishu-devops",
3
- "version": "26.627.1",
3
+ "version": "26.627.2",
4
4
  "description": "飞书消息 bot:扫码创建应用,通过 Claude Code / Codex 处理消息",
5
5
  "type": "module",
6
6
  "bin": {