larkway 0.3.8 → 0.3.10

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/main.js CHANGED
@@ -107360,7 +107360,6 @@ var require_main = __commonJS({
107360
107360
  });
107361
107361
 
107362
107362
  // src/main.ts
107363
- import { execSync } from "node:child_process";
107364
107363
  import { mkdirSync } from "node:fs";
107365
107364
  import path13 from "node:path";
107366
107365
 
@@ -112529,9 +112528,11 @@ ${recent.join("\n")}` : recent.join("\n");
112529
112528
  });
112530
112529
  elements.push({ tag: "hr" });
112531
112530
  }
112531
+ const mentionPrefix = atMentionMarkdown(opts.mentionOpenId);
112532
112532
  const cleanBody = opts.bodyText ? stripLeakedToolMarkup(opts.bodyText) : "";
112533
- if (cleanBody) {
112534
- const chunks = chunkMarkdown(cleanBody);
112533
+ const bodyWithMention = [mentionPrefix, cleanBody].filter(Boolean).join("\n\n");
112534
+ if (bodyWithMention) {
112535
+ const chunks = chunkMarkdown(bodyWithMention);
112535
112536
  for (let i = 0; i < chunks.length; i++) {
112536
112537
  elements.push({
112537
112538
  tag: "markdown",
@@ -112573,6 +112574,11 @@ ${chunks[i]}`
112573
112574
  };
112574
112575
  return JSON.stringify(card);
112575
112576
  }
112577
+ function atMentionMarkdown(userId) {
112578
+ if (!userId) return "";
112579
+ if (!/^[A-Za-z0-9_:-]+$/.test(userId)) return "";
112580
+ return `<at id=${userId}></at>`;
112581
+ }
112576
112582
  var CardHandleImpl = class {
112577
112583
  messageId;
112578
112584
  /** Transport for the leaf PATCH network call (default = lark-cli). */
@@ -112624,6 +112630,7 @@ var CardHandleImpl = class {
112624
112630
  showToolSummary: this.showToolSummary,
112625
112631
  status: cardStatus,
112626
112632
  failureReason: opts.failureReason,
112633
+ mentionOpenId: opts.mentionOpenId,
112627
112634
  // V2 dynamic-choice buttons — agent-declared, only on finalize.
112628
112635
  choices: opts.choices,
112629
112636
  choicePrompt: opts.choicePrompt,
@@ -113353,6 +113360,33 @@ function renderTurnTakingHint(limit) {
113353
113360
  "</turn-taking>"
113354
113361
  ];
113355
113362
  }
113363
+ function renderRuntimeWarningsBlock(warnings) {
113364
+ if (warnings.length === 0) return [];
113365
+ const hasMissingLarkCli = warnings.some((warning) => warning.command === "lark-cli");
113366
+ return [
113367
+ "<runtime-warnings>",
113368
+ "Bridge \u68C0\u6D4B\u5230\u4EE5\u4E0B\u672C\u673A\u80FD\u529B\u6682\u4E0D\u53EF\u7528\u3002\u8FD9\u662F\u63D0\u793A,\u4E0D\u662F\u5F3A\u5236\u505C\u6B62\u6761\u4EF6:",
113369
+ ...warnings.map((warning) => {
113370
+ const name = warning.command ? `${warning.label} (${warning.command})` : warning.label;
113371
+ const reason = warning.reason ? `: ${warning.reason}` : "";
113372
+ const installHint = warning.installHint ? ` Fix hint: ${warning.installHint}` : "";
113373
+ return `- ${name}${reason}${installHint}`;
113374
+ }),
113375
+ "",
113376
+ "\u5904\u7406\u539F\u5219:",
113377
+ "- \u80FD\u4EC5\u51ED\u5F53\u524D\u6D88\u606F\u7EE7\u7EED\u7684\u4EFB\u52A1,\u7EE7\u7EED\u5904\u7406,\u4E0D\u8981\u56E0\u4E3A warning \u76F4\u63A5\u62D2\u7EDD\u3002",
113378
+ "- \u53EA\u6709\u5F53\u4EFB\u52A1\u786E\u5B9E\u9700\u8981\u7F3A\u5931\u80FD\u529B\u65F6,\u518D\u5728 last_message \u91CC\u7528\u4EA7\u54C1\u5316\u8BED\u8A00\u544A\u8BC9\u7528\u6237\u7F3A\u4EC0\u4E48\u3001\u4F1A\u5F71\u54CD\u4EC0\u4E48\u3001\u5982\u4F55\u7EE7\u7EED\u3002",
113379
+ ...hasMissingLarkCli ? [
113380
+ "- \u5BF9\u7F3A\u5C11 lark-cli \u7684\u60C5\u51B5:\u4E0D\u8981\u989D\u5916 @ \u7528\u6237;\u5728\u5361\u7247\u91CC\u8F7B\u91CF\u8BF4\u660E\u5F53\u524D\u65E0\u6CD5\u81EA\u52A8\u8BFB\u53D6\u98DE\u4E66\u8BDD\u9898\u5386\u53F2\u3001\u9644\u4EF6\u6216\u6587\u6863\u5373\u53EF\u3002",
113381
+ '- \u5982\u679C\u5F53\u524D\u4EFB\u52A1\u9700\u8981\u8FD9\u4E9B\u4E0A\u4E0B\u6587,\u7528 choices \u95EE\u662F\u5426\u5141\u8BB8\u5B89\u88C5\u6700\u65B0\u7248\u98DE\u4E66 CLI\u3002\u5EFA\u8BAE: `choice_prompt: "\u8BFB\u53D6\u98DE\u4E66\u5386\u53F2\u9700\u8981\u672C\u673A\u5B89\u88C5\u6700\u65B0\u7248\u98DE\u4E66 CLI,\u662F\u5426\u5141\u8BB8\u6211\u5C1D\u8BD5\u5B89\u88C5?"`, `choices: [{label:"\u5141\u8BB8\u5B89\u88C5", value:"\u5141\u8BB8\u5B89\u88C5 lark-cli"}, {label:"\u5148\u4E0D\u5B89\u88C5", value:"\u5148\u4E0D\u5B89\u88C5 lark-cli,\u6211\u4F1A\u628A\u8981\u5904\u7406\u7684\u5185\u5BB9\u8D34\u5230\u8BDD\u9898\u91CC"}]`\u3002',
113382
+ "- \u7528\u6237\u660E\u786E\u9009\u62E9/\u56DE\u590D\u5141\u8BB8\u5B89\u88C5\u540E,\u518D\u5C1D\u8BD5\u5B89\u88C5;\u4E0D\u8981\u5728\u672A\u786E\u8BA4\u524D\u6539\u5BBF\u4E3B\u673A\u5168\u5C40\u73AF\u5883\u3002",
113383
+ "- \u63A8\u8350\u5B89\u88C5\u547D\u4EE4: `npx -y @larksuite/cli@latest install`,\u7136\u540E\u8FD0\u884C `lark-cli --version` \u9A8C\u8BC1\u3002",
113384
+ '- \u5982\u9047 npm \u5168\u5C40\u76EE\u5F55\u6743\u9650\u9519\u8BEF(EACCES/permission denied),\u4F7F\u7528\u7528\u6237\u7EA7 prefix \u540E\u91CD\u8BD5: `mkdir -p ~/.npm-global && npm config set prefix "$HOME/.npm-global" && export PATH="$HOME/.npm-global/bin:$PATH" && npx -y @larksuite/cli@latest install`\u3002\u4E0D\u8981\u9ED8\u8BA4\u8981\u6C42 sudo\u3002',
113385
+ "- \u5B89\u88C5\u6210\u529F\u540E,\u5982\u679C\u672C\u8F6E\u9700\u8981\u7ACB\u5373\u8BFB\u53D6\u98DE\u4E66\u4E0A\u4E0B\u6587,\u53EF\u5728\u5F53\u524D shell \u4E2D\u5E26\u4E0A\u4FEE\u590D\u540E\u7684 PATH \u7EE7\u7EED\u5C1D\u8BD5;\u82E5\u9700\u8981 bridge \u540E\u7EED\u8F6E\u6B21\u7A33\u5B9A\u4F7F\u7528,\u8BF7\u63D0\u793A owner \u91CD\u542F Larkway\u3002"
113386
+ ] : [],
113387
+ "</runtime-warnings>"
113388
+ ];
113389
+ }
113356
113390
  function renderWorkspaceBlock(primarySlug, primaryCachePath, defaultBranch, extraRepos) {
113357
113391
  const lines = [
113358
113392
  "<workspace>",
@@ -113423,7 +113457,17 @@ function sceneFacts(parsed, isNewThread) {
113423
113457
  };
113424
113458
  }
113425
113459
  function renderPrompt(input) {
113426
- const { parsed, isNewThread, conventions, peers, turn_taking_limit, agentMemory, extraRepoPaths, larkCliProfile } = input;
113460
+ const {
113461
+ parsed,
113462
+ isNewThread,
113463
+ conventions,
113464
+ peers,
113465
+ turn_taking_limit,
113466
+ agentMemory,
113467
+ extraRepoPaths,
113468
+ larkCliProfile,
113469
+ runtimeWarnings = []
113470
+ } = input;
113427
113471
  const backend = input.backend ?? "claude";
113428
113472
  const profileFlag = larkCliProfile ? ` --profile ${larkCliProfile}` : "";
113429
113473
  const attachmentKeys = parsed.attachments.map((a) => a.fileKey);
@@ -113436,6 +113480,7 @@ function renderPrompt(input) {
113436
113480
  const stateContract = renderStateContract(conventions.stateFilePath);
113437
113481
  const peersBlock = peers && peers.length > 0 ? renderPeersBlock(peers) : [];
113438
113482
  const turnTakingBlock = turn_taking_limit && turn_taking_limit > 0 ? renderTurnTakingHint(turn_taking_limit) : [];
113483
+ const runtimeWarningsBlock = renderRuntimeWarningsBlock(runtimeWarnings);
113439
113484
  const extraRepos = extraRepoPaths ?? conventions.extraRepoPaths ?? [];
113440
113485
  const workspaceBlock = isAgentWorkspace ? renderAgentWorkspaceBlock(conventions, extraRepos) : hasRepo ? renderWorkspaceBlock(
113441
113486
  conventions.defaultProjectSlug ?? "repo",
@@ -113491,6 +113536,7 @@ function renderPrompt(input) {
113491
113536
  "",
113492
113537
  ...agentMemoryBlock,
113493
113538
  ...skillIntroNew,
113539
+ ...runtimeWarningsBlock.length > 0 ? [...runtimeWarningsBlock, ""] : [],
113494
113540
  "<thread-context>",
113495
113541
  `thread_id: ${parsed.threadId}`,
113496
113542
  `message_id: ${parsed.messageId}`,
@@ -113558,6 +113604,7 @@ function renderPrompt(input) {
113558
113604
  return [
113559
113605
  ...agentMemoryBlock,
113560
113606
  ...skillIntroCont,
113607
+ ...runtimeWarningsBlock.length > 0 ? [...runtimeWarningsBlock, ""] : [],
113561
113608
  "<thread-context>",
113562
113609
  `thread_id: ${parsed.threadId}`,
113563
113610
  `message_id: ${parsed.messageId}`,
@@ -114441,6 +114488,11 @@ var BridgeHandler = class {
114441
114488
  constructor(deps) {
114442
114489
  this.deps = deps;
114443
114490
  }
114491
+ runtimeWarnings() {
114492
+ return (this.deps.runtimeRequirements ?? []).filter(
114493
+ (req) => !req.ok && (req.severity === "required" || req.kind === "secret")
114494
+ );
114495
+ }
114444
114496
  /**
114445
114497
  * Enter the main loop: for-await over client.events(), per-thread concurrent dispatch.
114446
114498
  *
@@ -114742,7 +114794,8 @@ var BridgeHandler = class {
114742
114794
  botName: this.deps.botConfig?.name,
114743
114795
  backend: this.deps.botConfig?.backend,
114744
114796
  agentMemory: this.deps.agentMemory,
114745
- larkCliProfile: this.deps.larkCliProfile
114797
+ larkCliProfile: this.deps.larkCliProfile,
114798
+ runtimeWarnings: this.runtimeWarnings()
114746
114799
  });
114747
114800
  const backend = this.deps.botConfig?.backend ?? "claude";
114748
114801
  const permissionMode = this.deps.permissionMode ?? (isAgentWorkspace ? "acceptEdits" : "bypassPermissions");
@@ -117852,6 +117905,14 @@ var GitIdentitySchema = external_exports.object({
117852
117905
  name: external_exports.string().min(1),
117853
117906
  email: external_exports.string().email()
117854
117907
  });
117908
+ var GitCloneUrlSchema = external_exports.string().min(1).refine(
117909
+ (value) => {
117910
+ if (/^https?:\/\/\S+$/i.test(value)) return true;
117911
+ if (/^ssh:\/\/\S+$/i.test(value)) return true;
117912
+ return /^[A-Za-z0-9_.-]+@[^:\s]+:[^ \t\r\n]+$/.test(value);
117913
+ },
117914
+ "url must be an http(s), ssh://, or scp-like Git clone URL"
117915
+ );
117855
117916
  var BotConfigSchema = external_exports.object({
117856
117917
  /** Unique identifier, kebab-case. Used as key in sessionStore. */
117857
117918
  id: external_exports.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case"),
@@ -117926,7 +117987,7 @@ var BotConfigSchema = external_exports.object({
117926
117987
  * Agent workspace runtime: URL is a pointer only; the Agent decides clone
117927
117988
  * timing and destination.
117928
117989
  */
117929
- url: external_exports.string().url().optional()
117990
+ url: GitCloneUrlSchema.optional()
117930
117991
  })
117931
117992
  ).default([]),
117932
117993
  /**
@@ -118284,12 +118345,12 @@ var SIGKILL_GRACE_MS = 5e3;
118284
118345
  function buildEnv(botGitIdentity, gitlabToken) {
118285
118346
  const env = { ...process.env };
118286
118347
  delete env["ANTHROPIC_API_KEY"];
118287
- const name = botGitIdentity?.name ?? "larkway-bot";
118288
- const email = botGitIdentity?.email ?? "bot@larkway.local";
118289
- env["GIT_AUTHOR_NAME"] = name;
118290
- env["GIT_AUTHOR_EMAIL"] = email;
118291
- env["GIT_COMMITTER_NAME"] = name;
118292
- env["GIT_COMMITTER_EMAIL"] = email;
118348
+ if (botGitIdentity) {
118349
+ env["GIT_AUTHOR_NAME"] = botGitIdentity.name;
118350
+ env["GIT_AUTHOR_EMAIL"] = botGitIdentity.email;
118351
+ env["GIT_COMMITTER_NAME"] = botGitIdentity.name;
118352
+ env["GIT_COMMITTER_EMAIL"] = botGitIdentity.email;
118353
+ }
118293
118354
  if (gitlabToken !== void 0) {
118294
118355
  env["GITLAB_TOKEN"] = gitlabToken;
118295
118356
  }
@@ -118598,12 +118659,12 @@ function buildCodexEnv(botGitIdentity, gitlabToken) {
118598
118659
  const env = { ...process.env };
118599
118660
  delete env["OPENAI_API_KEY"];
118600
118661
  delete env["ANTHROPIC_API_KEY"];
118601
- const name = botGitIdentity?.name ?? "larkway-bot";
118602
- const email = botGitIdentity?.email ?? "bot@larkway.local";
118603
- env["GIT_AUTHOR_NAME"] = name;
118604
- env["GIT_AUTHOR_EMAIL"] = email;
118605
- env["GIT_COMMITTER_NAME"] = name;
118606
- env["GIT_COMMITTER_EMAIL"] = email;
118662
+ if (botGitIdentity) {
118663
+ env["GIT_AUTHOR_NAME"] = botGitIdentity.name;
118664
+ env["GIT_AUTHOR_EMAIL"] = botGitIdentity.email;
118665
+ env["GIT_COMMITTER_NAME"] = botGitIdentity.name;
118666
+ env["GIT_COMMITTER_EMAIL"] = botGitIdentity.email;
118667
+ }
118607
118668
  if (gitlabToken !== void 0) {
118608
118669
  env["GITLAB_TOKEN"] = gitlabToken;
118609
118670
  }
@@ -118989,33 +119050,144 @@ function checkGrantMatchesBot(normalizedGrantText, bot) {
118989
119050
  return { ok: true };
118990
119051
  }
118991
119052
 
118992
- // src/main.ts
118993
- var STATUS_WRITE_INTERVAL_MS = 3e4;
118994
- var VERSION = resolveLarkwayVersion(import.meta.url);
118995
- function checkCli(name) {
119053
+ // src/runtimeRequirements.ts
119054
+ import { execFileSync } from "node:child_process";
119055
+ function checkCli(command) {
118996
119056
  try {
118997
- const p = execSync(`which ${name}`, { stdio: ["pipe", "pipe", "pipe"] }).toString().trim();
118998
- if (!p) return { ok: false, version: "" };
118999
- const version = execSync(`${name} --version`, {
119000
- stdio: ["pipe", "pipe", "pipe"]
119001
- }).toString().trim().split("\n")[0] ?? "";
119002
- return { ok: true, version };
119057
+ execFileSync("which", [command], { stdio: ["pipe", "pipe", "pipe"] });
119003
119058
  } catch {
119004
- return { ok: false, version: "" };
119059
+ return { ok: false };
119005
119060
  }
119061
+ try {
119062
+ const version = execFileSync(command, ["--version"], { stdio: ["pipe", "pipe", "pipe"] }).toString().trim().split("\n")[0];
119063
+ return { ok: true, ...version ? { version } : {} };
119064
+ } catch {
119065
+ return { ok: true };
119066
+ }
119067
+ }
119068
+ function repoLooksGitLab(urlOrSlug) {
119069
+ return /(^|[./:@-])gitlab([./:@-]|$)/i.test(urlOrSlug);
119006
119070
  }
119007
- function externalClisForBots(bots) {
119008
- const backendBins = bots.map((bot) => bot.backend ?? "claude");
119009
- return Array.from(/* @__PURE__ */ new Set(["lark-cli", ...backendBins, "glab"]));
119071
+ function botUsesGitLab(bot) {
119072
+ return bot.repos.some((repo) => repoLooksGitLab(repo.url ?? repo.slug));
119010
119073
  }
119074
+ function addCliRequirement(requirements, input) {
119075
+ const existing = requirements.get(`cli:${input.command}`);
119076
+ if (existing) {
119077
+ existing.botIds = Array.from(/* @__PURE__ */ new Set([...existing.botIds, ...input.botIds])).sort();
119078
+ if (existing.severity === "optional" && input.severity === "required") {
119079
+ existing.severity = "required";
119080
+ }
119081
+ return;
119082
+ }
119083
+ const probe = checkCli(input.command);
119084
+ requirements.set(`cli:${input.command}`, {
119085
+ id: `cli:${input.command}`,
119086
+ label: input.label ?? input.command,
119087
+ command: input.command,
119088
+ kind: "cli",
119089
+ severity: input.severity,
119090
+ ok: probe.ok,
119091
+ ...probe.version ? { version: probe.version } : {},
119092
+ reason: input.reason,
119093
+ ...input.installHint ? { installHint: input.installHint } : {},
119094
+ botIds: Array.from(new Set(input.botIds)).sort()
119095
+ });
119096
+ }
119097
+ function addSecretRequirement(requirements, input) {
119098
+ const id = `secret:${input.envName}`;
119099
+ const existing = requirements.get(id);
119100
+ if (existing) {
119101
+ existing.botIds = Array.from(/* @__PURE__ */ new Set([...existing.botIds, ...input.botIds])).sort();
119102
+ return;
119103
+ }
119104
+ const value = process.env[input.envName];
119105
+ requirements.set(id, {
119106
+ id,
119107
+ label: input.envName,
119108
+ kind: "secret",
119109
+ severity: input.severity ?? "required",
119110
+ ok: value != null && value !== "",
119111
+ reason: input.reason,
119112
+ installHint: `Set ${input.envName} in ~/.larkway/.env and restart Larkway.`,
119113
+ botIds: Array.from(new Set(input.botIds)).sort()
119114
+ });
119115
+ }
119116
+ function runtimeRequirementsForBots(bots) {
119117
+ const requirements = /* @__PURE__ */ new Map();
119118
+ if (bots.length > 0) {
119119
+ addCliRequirement(requirements, {
119120
+ command: "lark-cli",
119121
+ label: "Feishu CLI",
119122
+ severity: "required",
119123
+ botIds: bots.map((bot) => bot.id),
119124
+ reason: "Required for agents to read Feishu topic history, attachments, docs, and other context.",
119125
+ installHint: "Install and configure lark-cli, then restart Larkway."
119126
+ });
119127
+ }
119128
+ for (const bot of bots) {
119129
+ addCliRequirement(requirements, {
119130
+ command: bot.backend ?? "claude",
119131
+ severity: "required",
119132
+ botIds: [bot.id],
119133
+ reason: `Required to run this bot's ${bot.backend ?? "claude"} backend.`
119134
+ });
119135
+ if (bot.runtime === "agent_workspace" && bot.repos.length > 0) {
119136
+ const tokenEnvName = bot.git_token_env ?? bot.gitlab_token_env;
119137
+ if (tokenEnvName) {
119138
+ addSecretRequirement(requirements, {
119139
+ envName: tokenEnvName,
119140
+ botIds: [bot.id],
119141
+ severity: "optional",
119142
+ reason: "Optional Git identity material. When absent, agents use the host's normal Git auth."
119143
+ });
119144
+ } else {
119145
+ requirements.set(`secret:missing-git-token:${bot.id}`, {
119146
+ id: `secret:missing-git-token:${bot.id}`,
119147
+ label: "Git access token env",
119148
+ kind: "secret",
119149
+ severity: "optional",
119150
+ ok: false,
119151
+ reason: `Bot "${bot.id}" has repo pointers but no git_token_env. It will use the host's normal Git auth.`,
119152
+ installHint: "Add a Git access token only when you want this agent to use a specific GitHub/GitLab identity.",
119153
+ botIds: [bot.id]
119154
+ });
119155
+ }
119156
+ }
119157
+ if (botUsesGitLab(bot)) {
119158
+ addCliRequirement(requirements, {
119159
+ command: "glab",
119160
+ label: "GitLab CLI",
119161
+ severity: "optional",
119162
+ botIds: [bot.id],
119163
+ reason: "Only needed when an agent uses GitLab-specific actions such as MRs, pipelines, or releases.",
119164
+ installHint: "Install glab only for bots that need GitLab-specific workflow commands."
119165
+ });
119166
+ }
119167
+ }
119168
+ return [...requirements.values()].sort((a, b) => {
119169
+ if (a.severity !== b.severity) return a.severity === "required" ? -1 : 1;
119170
+ if (a.kind !== b.kind) return a.kind === "cli" ? -1 : 1;
119171
+ return a.label.localeCompare(b.label);
119172
+ });
119173
+ }
119174
+
119175
+ // src/main.ts
119176
+ var STATUS_WRITE_INTERVAL_MS = 3e4;
119177
+ var VERSION = resolveLarkwayVersion(import.meta.url);
119011
119178
  function printExternalCliProbe(bots) {
119012
- console.log("External CLIs:");
119013
- for (const name of externalClisForBots(bots)) {
119014
- const { ok, version } = checkCli(name);
119015
- if (ok) {
119016
- console.log(` ${name.padEnd(10)} \u2713 ${version}`);
119179
+ console.log("Runtime requirements:");
119180
+ for (const req of runtimeRequirementsForBots(bots)) {
119181
+ const icon = req.ok ? "\u2713" : "\u2717";
119182
+ const tag = req.severity === "optional" ? "optional" : "required";
119183
+ const target = req.command ?? req.label;
119184
+ const botScope = req.botIds.length > 0 ? ` [${req.botIds.join(", ")}]` : "";
119185
+ if (req.ok) {
119186
+ console.log(` ${target.padEnd(14)} ${icon} ${req.version ?? tag}${botScope}`);
119017
119187
  } else {
119018
- console.warn(` ${name.padEnd(10)} \u2717 (not found \u2014 install before running bots that use it)`);
119188
+ const message = req.kind === "secret" ? req.reason : `not found \u2014 ${req.reason}`;
119189
+ console.warn(` ${target.padEnd(14)} ${icon} (${tag}) ${message}${botScope}`);
119190
+ if (req.installHint) console.warn(` ${"".padEnd(14)} ${req.installHint}`);
119019
119191
  }
119020
119192
  }
119021
119193
  }
@@ -119049,18 +119221,16 @@ async function runV2Mode({
119049
119221
  if (bot.runtime === "agent_workspace") {
119050
119222
  const _agentTokenEnvName = bot.git_token_env ?? bot.gitlab_token_env;
119051
119223
  if (bot.repos.length > 0 && !_agentTokenEnvName) {
119052
- console.error(
119053
- `[larkway] SKIPPING bot "${bot.id}": runtime=agent_workspace has repo pointers but no git_token_env (or gitlab_token_env). v0.3 workspace repo bots must reference an explicit per-agent token env name; they do not inherit global GITLAB_TOKEN.`
119224
+ console.warn(
119225
+ `[larkway] bot "${bot.id}": runtime=agent_workspace has repo pointers but no git_token_env. Starting anyway; the agent will use the host's normal Git identity/auth.`
119054
119226
  );
119055
- continue;
119056
119227
  }
119057
119228
  if (_agentTokenEnvName) {
119058
119229
  const agentToken = process.env[_agentTokenEnvName];
119059
119230
  if (agentToken == null || agentToken === "") {
119060
- console.error(
119061
- `[larkway] SKIPPING bot "${bot.id}": runtime=agent_workspace declares token env "${_agentTokenEnvName}", but that env var is unset/empty. Set the real token in ~/.larkway/.env; v0.3 workspace bots do not fall back to global GITLAB_TOKEN.`
119231
+ console.warn(
119232
+ `[larkway] bot "${bot.id}": runtime=agent_workspace declares token env "${_agentTokenEnvName}", but that env var is unset/empty. Starting anyway; the agent will use the host's normal Git identity/auth.`
119062
119233
  );
119063
- continue;
119064
119234
  }
119065
119235
  }
119066
119236
  const permissionGate = await checkWorkspacePermissionGrant(resolveAgentWorkspacePath(bot.id), bot);
@@ -119091,7 +119261,7 @@ async function runV2Mode({
119091
119261
  const gitlabToken = tokenEnvName != null ? process.env[tokenEnvName] : void 0;
119092
119262
  if (tokenEnvName != null && (gitlabToken == null || gitlabToken === "")) {
119093
119263
  console.warn(
119094
- `[larkway] bot "${bot.id}" declares token env "${tokenEnvName}" but that env var is unset/empty \u2014 falling back to global GITLAB_TOKEN.`
119264
+ `[larkway] bot "${bot.id}" declares token env "${tokenEnvName}" but that env var is unset/empty \u2014 leaving the host Git auth environment unchanged.`
119095
119265
  );
119096
119266
  }
119097
119267
  const larkCliProfile = deriveLarkCliProfile(bot.lark_cli_profile, bot.app_id);
@@ -119162,7 +119332,7 @@ async function runV2Mode({
119162
119332
  readOnly: bot.read_only,
119163
119333
  gitlabTokenEnvName: tokenEnvName
119164
119334
  };
119165
- const effectiveGitlabToken = bot.runtime === "agent_workspace" ? gitlabToken ?? "" : gitlabToken;
119335
+ const effectiveGitlabToken = gitlabToken;
119166
119336
  const resolvedPeers = bot.peers.flatMap((peerId) => {
119167
119337
  const peer = bots.find((b) => b.id === peerId);
119168
119338
  if (!peer) return [];
@@ -119189,6 +119359,7 @@ async function runV2Mode({
119189
119359
  gitlabToken: effectiveGitlabToken,
119190
119360
  agentMemory: bot.agent_memory,
119191
119361
  larkCliProfile,
119362
+ runtimeRequirements: runtimeRequirementsForBots([bot]),
119192
119363
  recordRuntimeEvent: async (patch) => {
119193
119364
  await upsertRuntimeEvent(larkwayHome(), bot.id, patch);
119194
119365
  }