@lumi.ai/runner 0.15.17 → 0.15.19

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 +103 -89
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -8,8 +8,8 @@ import os4 from "node:os";
8
8
  import {
9
9
  collection as collection7,
10
10
  deleteField as deleteField2,
11
- doc as doc8,
12
- getDoc as getDoc7,
11
+ doc as doc7,
12
+ getDoc as getDoc6,
13
13
  getDocs as getDocs6,
14
14
  onSnapshot as onSnapshot2,
15
15
  orderBy as orderBy4,
@@ -63,6 +63,9 @@ var ENGINES = {
63
63
  claude: CLAUDE_ENGINE
64
64
  };
65
65
  var DEFAULT_ENGINE_ID = "claude";
66
+ function listEngines() {
67
+ return Object.values(ENGINES);
68
+ }
66
69
  function isKnownEngine(id) {
67
70
  return !!id && Object.hasOwn(ENGINES, id);
68
71
  }
@@ -82,6 +85,8 @@ function estimateCostUsd(engineId, modelId, tokens) {
82
85
  const fresh = Math.max(0, tokens.inputTokens - tokens.cachedInputTokens);
83
86
  return (fresh * model.rates.input + tokens.cachedInputTokens * model.rates.cachedInput + tokens.outputTokens * model.rates.output) / 1e6;
84
87
  }
88
+ var AGENT_ENGINE_IDS = Object.freeze(listEngines().map((e) => e.id));
89
+ var AGENT_MODEL_IDS = Object.freeze(listEngines().flatMap((e) => e.models.map((m) => m.id)));
85
90
 
86
91
  // ../shared/dist/agent.js
87
92
  var DEFAULT_AGENT_TOOLS = {
@@ -144,6 +149,41 @@ var ASSISTANT_PROFILE = Object.freeze({
144
149
  })
145
150
  });
146
151
 
152
+ // ../shared/dist/assistantAuthority.js
153
+ var ASSISTANT_TOOL_AUTHORITY = Object.freeze({
154
+ // Reads. Everything the asker could open in the app.
155
+ ship_info: "member",
156
+ agent_list: "member",
157
+ task_list: "member",
158
+ task_get: "member",
159
+ knowledge_get: "member",
160
+ chat_get: "member",
161
+ // Writes a member can already make by clicking.
162
+ task_create: "member",
163
+ task_comment: "member",
164
+ task_update_status: "member",
165
+ task_relate: "member",
166
+ task_assign: "member",
167
+ knowledge_write: "member",
168
+ chat_send: "member",
169
+ // Captain-only, because the rules say so for a direct write — and `agent_write` also bills.
170
+ agent_write: "captain",
171
+ workflow_write: "captain",
172
+ knowledge_folder: "captain"
173
+ });
174
+ var ASSISTANT_NEVER_REGISTER = Object.freeze([
175
+ "approval_decide",
176
+ "approval_request",
177
+ "memory_write",
178
+ "media_attach"
179
+ ]);
180
+ var ASSISTANT_FROZEN_ARGS = Object.freeze({
181
+ agent_write: Object.freeze(["tools"])
182
+ });
183
+
184
+ // ../shared/dist/toolStats.js
185
+ var TOOL_STATS_HALF_LIFE_MS = 14 * 24 * 60 * 60 * 1e3;
186
+
147
187
  // ../shared/dist/browser.js
148
188
  var BROWSER_ONLINE_WINDOW_MS = 9e4;
149
189
  var DEFAULT_BROWSER_CONSENT_MS = 60 * 60 * 1e3;
@@ -332,6 +372,8 @@ var COLLECTIONS = {
332
372
  engineLimits: "engine_limits",
333
373
  /** `ships/{shipId}/events/{id}` — append-only Ship-level audit log. */
334
374
  events: "events",
375
+ /** §15.70: per-actor tool usage, so the assistant declares what it actually reaches for. */
376
+ toolStats: "toolStats",
335
377
  /** `ships/{shipId}/secrets/{docId}` — runner creds + MCP token hash (see secrets.ts). */
336
378
  secrets: "secrets",
337
379
  /**
@@ -844,21 +886,6 @@ function firstStatusIn(statuses, category) {
844
886
  var RUNNER_OFFLINE_AFTER_MS = 2 * 60 * 1e3;
845
887
 
846
888
  // ../shared/dist/secrets.js
847
- var SECRET_DOCS = {
848
- /** Captain-provided runner credentials (this file's shape). */
849
- runner: "runner",
850
- /** `{ tokenHash }` for the Workspace MCP bearer — backend-only, never client-readable. */
851
- mcp: "mcp",
852
- /**
853
- * `{ runnerId, keyHash }` for THIS Ship's daemon credential (PRD §8.1) — backend-only.
854
- *
855
- * One document, not one per machine, because a Ship runs one daemon: issuing a key is
856
- * therefore also how the previous one is revoked, exactly like rotating the MCP bearer.
857
- * Only `docId == 'runner'` is released by packages/crew/firestore.rules, so this falls
858
- * through to deny for every client without needing a rule of its own.
859
- */
860
- runnerKey: "runnerKey"
861
- };
862
889
  function missingSecretsFor(engineId, secrets) {
863
890
  const bag = secrets;
864
891
  return getEngine(engineId).requiredSecrets.filter((req) => !bag?.[req.key]).map((req) => req.label);
@@ -1038,7 +1065,7 @@ function mcpUrl(config2) {
1038
1065
  }
1039
1066
 
1040
1067
  // src/version.ts
1041
- var RUNNER_VERSION = true ? "0.15.17" : "0.0.0-dev";
1068
+ var RUNNER_VERSION = true ? "0.15.19" : "0.0.0-dev";
1042
1069
 
1043
1070
  // src/auth.ts
1044
1071
  import { signInWithCustomToken } from "firebase/auth";
@@ -2420,13 +2447,12 @@ async function resolveGithubToken(input) {
2420
2447
  throw e;
2421
2448
  }
2422
2449
  }
2423
- if (integration && !input.githubPat) {
2450
+ if (integration) {
2424
2451
  return {
2425
2452
  source: "none",
2426
2453
  unavailable: `The Ship's GitHub connection is ${integration.status}. A captain can fix it in Ship Settings \u203A GitHub.`
2427
2454
  };
2428
2455
  }
2429
- if (input.githubPat) return { token: input.githubPat, source: "pat" };
2430
2456
  return null;
2431
2457
  }
2432
2458
 
@@ -2738,17 +2764,8 @@ function serversNeedingProbe(servers) {
2738
2764
  );
2739
2765
  }
2740
2766
 
2741
- // src/jobs/secrets.ts
2742
- import { doc as doc4, getDoc as getDoc4 } from "firebase/firestore";
2743
- async function loadRunnerSecrets(db, shipId) {
2744
- const snap = await withFirestoreRetry(
2745
- () => getDoc4(doc4(db, COLLECTIONS.ships, shipId, COLLECTIONS.secrets, SECRET_DOCS.runner))
2746
- );
2747
- return snap.exists() ? snap.data() : null;
2748
- }
2749
-
2750
2767
  // src/jobs/credentials.ts
2751
- import { collection as collection4, doc as doc5, getDoc as getDoc5, getDocs as getDocs4 } from "firebase/firestore";
2768
+ import { collection as collection4, doc as doc4, getDoc as getDoc4, getDocs as getDocs4 } from "firebase/firestore";
2752
2769
  async function loadShipCredentials(db, shipId) {
2753
2770
  const snap = await withFirestoreRetry(
2754
2771
  () => getDocs4(collection4(db, COLLECTIONS.ships, shipId, COLLECTIONS.credentials))
@@ -2757,35 +2774,41 @@ async function loadShipCredentials(db, shipId) {
2757
2774
  }
2758
2775
  async function loadCredentialToken(db, shipId, credentialId) {
2759
2776
  const snap = await withFirestoreRetry(
2760
- () => getDoc5(doc5(db, COLLECTIONS.ships, shipId, COLLECTIONS.secrets, credentialId))
2777
+ () => getDoc4(doc4(db, COLLECTIONS.ships, shipId, COLLECTIONS.secrets, credentialId))
2761
2778
  );
2762
2779
  if (!snap.exists()) return null;
2763
2780
  const token = snap.data().token;
2764
2781
  return typeof token === "string" && token.trim() ? token : null;
2765
2782
  }
2766
2783
  async function resolveJobSecrets(input) {
2767
- const { agent, credentials, runnerSecrets } = input;
2784
+ const { agent, credentials } = input;
2768
2785
  const engineId = agentEngine(agent);
2769
2786
  const resolved = resolveAgentCredential(agent, credentials, DEFAULT_ENGINE_ID);
2770
- if (resolved === void 0) return { secrets: runnerSecrets };
2787
+ if (resolved === void 0) {
2788
+ return {
2789
+ secrets: null,
2790
+ problem: `This workspace has no AI credential for ${engineId}. Add one in Settings \u203A AI credentials and the crew starts taking work again.`
2791
+ };
2792
+ }
2771
2793
  if (resolved === null) {
2772
2794
  return {
2773
- secrets: runnerSecrets,
2795
+ secrets: null,
2774
2796
  problem: `This agent is set to an AI credential that no longer exists on the Ship. Point it at another one in Settings \u203A AI credentials, or add the credential back.`
2775
2797
  };
2776
2798
  }
2777
2799
  const token = await loadCredentialToken(input.db, input.shipId, resolved.id);
2778
2800
  if (!token) {
2779
2801
  return {
2780
- secrets: runnerSecrets,
2802
+ secrets: null,
2781
2803
  problem: `The AI credential "${resolved.label}" has no token stored. Erase it in Settings \u203A AI credentials and add it again.`
2782
2804
  };
2783
2805
  }
2784
2806
  const key = getEngine(engineId).requiredSecrets[0]?.key;
2785
- if (!key) return { secrets: runnerSecrets };
2807
+ if (!key) return { secrets: null };
2786
2808
  return {
2787
2809
  secrets: {
2788
- ...runnerSecrets ?? { updatedAt: resolved.updatedAt, updatedBy: resolved.createdBy },
2810
+ updatedAt: resolved.updatedAt,
2811
+ updatedBy: resolved.createdBy,
2789
2812
  [key]: token,
2790
2813
  [`${key}Tail`]: resolved.tail
2791
2814
  }
@@ -2796,7 +2819,7 @@ async function resolveJobSecrets(input) {
2796
2819
  import {
2797
2820
  collection as collection5,
2798
2821
  deleteDoc,
2799
- doc as doc6,
2822
+ doc as doc5,
2800
2823
  onSnapshot,
2801
2824
  setDoc
2802
2825
  } from "firebase/firestore";
@@ -2825,7 +2848,7 @@ function nextResetAt(limits, now) {
2825
2848
  return soonest;
2826
2849
  }
2827
2850
  function limitRef(db, shipId, engineId) {
2828
- return doc6(db, COLLECTIONS.ships, shipId, COLLECTIONS.engineLimits, engineId);
2851
+ return doc5(db, COLLECTIONS.ships, shipId, COLLECTIONS.engineLimits, engineId);
2829
2852
  }
2830
2853
  async function noteEngineLimit(db, shipId, engineId, limit3, runnerId) {
2831
2854
  await setDoc(limitRef(db, shipId, engineId), {
@@ -2924,8 +2947,8 @@ import {
2924
2947
  addDoc,
2925
2948
  collection as collection6,
2926
2949
  deleteField,
2927
- doc as doc7,
2928
- getDoc as getDoc6,
2950
+ doc as doc6,
2951
+ getDoc as getDoc5,
2929
2952
  getDocs as getDocs5,
2930
2953
  limit as fsLimit,
2931
2954
  orderBy as orderBy3,
@@ -2971,10 +2994,10 @@ function backstopReportContent(resultText2) {
2971
2994
  return { report: report4, summary };
2972
2995
  }
2973
2996
  async function finalizeJob(db, shipId, job, input) {
2974
- const shipRef = doc7(db, COLLECTIONS.ships, shipId);
2975
- const jobRef = doc7(shipRef, COLLECTIONS.jobs, job.id);
2997
+ const shipRef = doc6(db, COLLECTIONS.ships, shipId);
2998
+ const jobRef = doc6(shipRef, COLLECTIONS.jobs, job.id);
2976
2999
  const now = Date.now();
2977
- const usageRef = doc7(shipRef, COLLECTIONS.usageDaily, utcDay(now));
3000
+ const usageRef = doc6(shipRef, COLLECTIONS.usageDaily, utcDay(now));
2978
3001
  await runTransaction(db, async (tx) => {
2979
3002
  const usageSnap = await tx.get(usageRef);
2980
3003
  const jobSnap = await tx.get(jobRef);
@@ -3036,7 +3059,7 @@ async function finalizeJob(db, shipId, job, input) {
3036
3059
  }
3037
3060
  async function requeueForRetry(db, shipId, job, error) {
3038
3061
  await runTransaction(db, async (tx) => {
3039
- tx.update(doc7(db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id), {
3062
+ tx.update(doc6(db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id), {
3040
3063
  status: "queued",
3041
3064
  attempt: job.attempt + 1,
3042
3065
  error: error.slice(0, 1500),
@@ -3046,7 +3069,7 @@ async function requeueForRetry(db, shipId, job, error) {
3046
3069
  });
3047
3070
  }
3048
3071
  async function releaseJob(db, shipId, job, reason) {
3049
- await updateDoc(doc7(db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id), {
3072
+ await updateDoc(doc6(db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id), {
3050
3073
  status: "queued",
3051
3074
  runnerId: "",
3052
3075
  startedAt: 0,
@@ -3062,13 +3085,13 @@ async function releaseJob(db, shipId, job, reason) {
3062
3085
  });
3063
3086
  }
3064
3087
  async function markTaskFailed(db, shipId, job, error, statuses) {
3065
- const shipRef = doc7(db, COLLECTIONS.ships, shipId);
3066
- const taskRef = doc7(shipRef, COLLECTIONS.tasks, job.taskId);
3088
+ const shipRef = doc6(db, COLLECTIONS.ships, shipId);
3089
+ const taskRef = doc6(shipRef, COLLECTIONS.tasks, job.taskId);
3067
3090
  const now = Date.now();
3068
3091
  const failedStatus = firstStatusIn(statuses, "failed")?.id ?? "failed";
3069
3092
  await runTransaction(db, async (tx) => {
3070
3093
  tx.update(taskRef, { status: failedStatus, updatedAt: now });
3071
- tx.set(doc7(collection6(taskRef, COLLECTIONS.activity)), {
3094
+ tx.set(doc6(collection6(taskRef, COLLECTIONS.activity)), {
3072
3095
  author: { type: "agent", id: job.agentId },
3073
3096
  createdAt: now,
3074
3097
  kind: "comment",
@@ -3082,7 +3105,7 @@ ${error.slice(0, 800)}
3082
3105
  });
3083
3106
  }
3084
3107
  async function markTaskStopped(db, shipId, job, stoppedBy) {
3085
- const taskRef = doc7(db, COLLECTIONS.ships, shipId, COLLECTIONS.tasks, job.taskId);
3108
+ const taskRef = doc6(db, COLLECTIONS.ships, shipId, COLLECTIONS.tasks, job.taskId);
3086
3109
  const who = await actorName(db, shipId, stoppedBy);
3087
3110
  await addDoc(collection6(taskRef, COLLECTIONS.activity), {
3088
3111
  author: { type: "agent", id: job.agentId },
@@ -3093,7 +3116,7 @@ async function markTaskStopped(db, shipId, job, stoppedBy) {
3093
3116
  }
3094
3117
  async function actorName(db, shipId, actorId) {
3095
3118
  try {
3096
- const snap = await getDoc6(doc7(db, COLLECTIONS.ships, shipId, COLLECTIONS.members, actorId));
3119
+ const snap = await getDoc5(doc6(db, COLLECTIONS.ships, shipId, COLLECTIONS.members, actorId));
3097
3120
  const m = snap.data();
3098
3121
  return m?.displayName || m?.email || actorId;
3099
3122
  } catch {
@@ -3101,7 +3124,7 @@ async function actorName(db, shipId, actorId) {
3101
3124
  }
3102
3125
  }
3103
3126
  async function markChatStopped(db, shipId, job, stoppedBy) {
3104
- const chatRef = doc7(db, COLLECTIONS.ships, shipId, COLLECTIONS.chats, job.chatId);
3127
+ const chatRef = doc6(db, COLLECTIONS.ships, shipId, COLLECTIONS.chats, job.chatId);
3105
3128
  const who = await actorName(db, shipId, stoppedBy);
3106
3129
  const content = `Stopped by ${who} before I finished. Write again to start a fresh run.`;
3107
3130
  await addDoc(collection6(chatRef, COLLECTIONS.chatMessages), {
@@ -3116,8 +3139,8 @@ async function markChatStopped(db, shipId, job, stoppedBy) {
3116
3139
  });
3117
3140
  }
3118
3141
  async function markChatFailed(db, shipId, job, error) {
3119
- const shipRef = doc7(db, COLLECTIONS.ships, shipId);
3120
- const chatRef = doc7(shipRef, COLLECTIONS.chats, job.chatId);
3142
+ const shipRef = doc6(db, COLLECTIONS.ships, shipId);
3143
+ const chatRef = doc6(shipRef, COLLECTIONS.chats, job.chatId);
3121
3144
  const now = Date.now();
3122
3145
  const content = `I could not finish replying \u2014 the run failed after ${job.attempt} attempt(s).
3123
3146
 
@@ -3125,7 +3148,7 @@ async function markChatFailed(db, shipId, job, error) {
3125
3148
  ${error.slice(0, 500)}
3126
3149
  \`\`\`
3127
3150
 
3128
- Write again to start a fresh run.`;
3151
+ Use Try again above to run it once more.`;
3129
3152
  await addDoc(collection6(chatRef, COLLECTIONS.chatMessages), {
3130
3153
  author: { type: "agent", id: job.agentId },
3131
3154
  content,
@@ -3145,7 +3168,7 @@ function backstopReplyContent(resultText2) {
3145
3168
  return `${text.slice(0, MAX_CHAT_MESSAGE_CHARS - marker.length)}${marker}`;
3146
3169
  }
3147
3170
  async function ensureChatReply(db, shipId, job, resultText2) {
3148
- const chatRef = doc7(db, COLLECTIONS.ships, shipId, COLLECTIONS.chats, job.chatId);
3171
+ const chatRef = doc6(db, COLLECTIONS.ships, shipId, COLLECTIONS.chats, job.chatId);
3149
3172
  const messagesCol = collection6(chatRef, COLLECTIONS.chatMessages);
3150
3173
  const snap = await getDocs5(
3151
3174
  query3(
@@ -3971,7 +3994,7 @@ async function startDaemon() {
3971
3994
  const warnedUnapproved = /* @__PURE__ */ new Set();
3972
3995
  const shipRunnerRef = (shipId) => {
3973
3996
  const session = sess(shipId);
3974
- return doc8(session.fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.runners, session.runnerId);
3997
+ return doc7(session.fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.runners, session.runnerId);
3975
3998
  };
3976
3999
  const needsRefill = /* @__PURE__ */ new Set();
3977
4000
  let beating = false;
@@ -3982,7 +4005,7 @@ async function startDaemon() {
3982
4005
  try {
3983
4006
  for (const shipId of [...serving]) {
3984
4007
  try {
3985
- const snap = await getDoc7(shipRunnerRef(shipId));
4008
+ const snap = await getDoc6(shipRunnerRef(shipId));
3986
4009
  if (!snap.exists()) {
3987
4010
  forgetShipLocally(shipId, "a captain removed this machine on the Daemons page");
3988
4011
  continue;
@@ -4327,7 +4350,7 @@ async function startDaemon() {
4327
4350
  pending.set(`${shipId}/${job.id}`, { shipId, job });
4328
4351
  return null;
4329
4352
  }
4330
- const jobRef = doc8(sess(shipId).fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id);
4353
+ const jobRef = doc7(sess(shipId).fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id);
4331
4354
  try {
4332
4355
  let claimed = null;
4333
4356
  await runTransaction2(sess(shipId).fb.db, async (tx) => {
@@ -4350,14 +4373,11 @@ async function startDaemon() {
4350
4373
  }
4351
4374
  async function setAgentStatus(shipId, agentId, status) {
4352
4375
  try {
4353
- await updateDoc2(doc8(sess(shipId).fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.agents, agentId), { status });
4376
+ await updateDoc2(doc7(sess(shipId).fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.agents, agentId), { status });
4354
4377
  } catch (e) {
4355
4378
  log2(`agent status update failed: ${e instanceof Error ? e.message : e}`);
4356
4379
  }
4357
4380
  }
4358
- async function loadSecrets(shipId) {
4359
- return loadRunnerSecrets(sess(shipId).fb.db, shipId);
4360
- }
4361
4381
  async function processJob(shipId, queuedJob, slot) {
4362
4382
  const job = await claim(shipId, queuedJob);
4363
4383
  if (!job) return;
@@ -4397,8 +4417,8 @@ async function startDaemon() {
4397
4417
  void (async () => {
4398
4418
  if (slot.stop) return;
4399
4419
  try {
4400
- const snap = await getDoc7(
4401
- doc8(sess(shipId).fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id)
4420
+ const snap = await getDoc6(
4421
+ doc7(sess(shipId).fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id)
4402
4422
  );
4403
4423
  const fresh = snap.data();
4404
4424
  if (!fresh?.stopRequestedAt || slot.stop) return;
@@ -4438,7 +4458,7 @@ async function startDaemon() {
4438
4458
  let statuses = DEFAULT_TASK_STATUSES;
4439
4459
  let knownSecrets = [];
4440
4460
  const progress = createProgressWriter({
4441
- write: (p) => updateDoc2(doc8(sess(shipId).fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id), {
4461
+ write: (p) => updateDoc2(doc7(sess(shipId).fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id), {
4442
4462
  progress: p
4443
4463
  }),
4444
4464
  secrets: () => knownSecrets,
@@ -4452,8 +4472,8 @@ async function startDaemon() {
4452
4472
  onDenied: () => {
4453
4473
  void (async () => {
4454
4474
  try {
4455
- const snap = await getDoc7(
4456
- doc8(sess(shipId).fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id)
4475
+ const snap = await getDoc6(
4476
+ doc7(sess(shipId).fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.jobs, job.id)
4457
4477
  );
4458
4478
  const fresh = snap.data();
4459
4479
  if (!fresh || fresh.status !== "running" || fresh.runnerId !== sess(shipId).runnerId) {
@@ -4468,7 +4488,7 @@ async function startDaemon() {
4468
4488
  });
4469
4489
  progress.push({ kind: "start", label: "Starting up" });
4470
4490
  try {
4471
- let secrets = await loadSecrets(shipId);
4491
+ let secrets = null;
4472
4492
  const packed = target.kind === "chat" ? { kind: "chat", ctx: await loadChatContext(sess(shipId).fb.db, shipId, { ...job, chatId: target.chatId }) } : { kind: "task", ctx: await loadJobContext(sess(shipId).fb.db, shipId, { ...job, taskId: target.taskId }) };
4473
4493
  if (packed.kind === "task" && !isWorkflowSentinel(job.workflowId) && !packed.ctx.workflow) {
4474
4494
  log2(
@@ -4483,7 +4503,6 @@ async function startDaemon() {
4483
4503
  db: sess(shipId).fb.db,
4484
4504
  shipId,
4485
4505
  agent,
4486
- runnerSecrets: secrets,
4487
4506
  credentials: await loadShipCredentials(sess(shipId).fb.db, shipId).catch(() => [])
4488
4507
  });
4489
4508
  if (resolvedSecrets.problem) {
@@ -4521,8 +4540,7 @@ async function startDaemon() {
4521
4540
  idToken,
4522
4541
  shipId,
4523
4542
  jobId: job.id,
4524
- agent,
4525
- githubPat: secrets?.githubPat
4543
+ agent
4526
4544
  });
4527
4545
  githubToken = gh?.token;
4528
4546
  githubRepos = gh?.repos;
@@ -4562,7 +4580,6 @@ async function startDaemon() {
4562
4580
  idToken,
4563
4581
  secrets?.claudeToken,
4564
4582
  githubToken,
4565
- secrets?.githubPat,
4566
4583
  // §15.31. BOTH the composed header value and the bare credential — see
4567
4584
  // `mcpSecretsToRedact` for why one of them is not enough.
4568
4585
  ...mcpSecretsToRedact(extraMcpServers)
@@ -4747,7 +4764,6 @@ async function startDaemon() {
4747
4764
  if (serving.has(shipId)) await setAgentStatus(shipId, job.agentId, "idle");
4748
4765
  if (!shuttingDown) void heartbeat();
4749
4766
  }
4750
- await Promise.all([...serving].map((shipId) => loadSecrets(shipId).catch(() => null)));
4751
4767
  await heartbeat();
4752
4768
  const heartbeatTimer = setInterval(() => {
4753
4769
  void heartbeat();
@@ -5288,7 +5304,7 @@ function setParallel(config2, value, ship2) {
5288
5304
  import { spawnSync as spawnSync3 } from "node:child_process";
5289
5305
  import fs7 from "node:fs";
5290
5306
  import path8 from "node:path";
5291
- import { collection as collection8, doc as doc9, getDoc as getDoc8, getDocs as getDocs7 } from "firebase/firestore";
5307
+ import { collection as collection8, doc as doc8, getDoc as getDoc7, getDocs as getDocs7 } from "firebase/firestore";
5292
5308
 
5293
5309
  // src/cli/session.ts
5294
5310
  async function openShipSession(shipId) {
@@ -5457,8 +5473,8 @@ async function checkShips(config2) {
5457
5473
  continue;
5458
5474
  }
5459
5475
  try {
5460
- const snap = await getDoc8(
5461
- doc9(session.fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.runners, session.runnerId)
5476
+ const snap = await getDoc7(
5477
+ doc8(session.fb.db, COLLECTIONS.ships, shipId, COLLECTIONS.runners, session.runnerId)
5462
5478
  );
5463
5479
  if (!snap.exists()) {
5464
5480
  checks.push(
@@ -5508,14 +5524,12 @@ async function checkShips(config2) {
5508
5524
  for (const id of shipEngines) engines.add(id);
5509
5525
  if (agents.some((agent) => effectiveAgentTools(agent).github.enabled)) needsGithub = true;
5510
5526
  try {
5511
- const secrets = await loadRunnerSecrets(session.fb.db, shipId);
5512
5527
  const credentials = await loadShipCredentials(session.fb.db, shipId).catch(() => []);
5513
5528
  const missing = [
5514
5529
  ...new Set(
5515
- [...shipEngines].flatMap((id) => {
5516
- if (credentials.some((c) => c.engine === id)) return [];
5517
- return missingSecretsFor(id, secrets);
5518
- })
5530
+ [...shipEngines].flatMap(
5531
+ (id) => credentials.some((c) => c.engine === id) ? [] : missingSecretsFor(id, null)
5532
+ )
5519
5533
  )
5520
5534
  ];
5521
5535
  const orphaned = agents.filter(
@@ -5999,7 +6013,7 @@ async function runUninstall(options) {
5999
6013
  }
6000
6014
 
6001
6015
  // src/cli/commands/ship.ts
6002
- import { doc as doc10, getDoc as getDoc9 } from "firebase/firestore";
6016
+ import { doc as doc9, getDoc as getDoc8 } from "firebase/firestore";
6003
6017
  async function listMyShips() {
6004
6018
  const config2 = requireConfig();
6005
6019
  const ids = Object.keys(config2.shipKeys ?? {});
@@ -6007,7 +6021,7 @@ async function listMyShips() {
6007
6021
  ids.map(async (id) => {
6008
6022
  try {
6009
6023
  const { fb } = await openShipSession(id);
6010
- const snap = await getDoc9(doc10(fb.db, COLLECTIONS.ships, id));
6024
+ const snap = await getDoc8(doc9(fb.db, COLLECTIONS.ships, id));
6011
6025
  return { id, ...snap.data() };
6012
6026
  } catch {
6013
6027
  return null;
@@ -6168,9 +6182,9 @@ async function runSetup(options) {
6168
6182
  // src/cli/commands/status.ts
6169
6183
  import {
6170
6184
  collection as collection9,
6171
- doc as doc11,
6185
+ doc as doc10,
6172
6186
  getCountFromServer as getCountFromServer2,
6173
- getDoc as getDoc10,
6187
+ getDoc as getDoc9,
6174
6188
  getDocs as getDocs8,
6175
6189
  query as query5,
6176
6190
  where as where5
@@ -6208,8 +6222,8 @@ async function runStatus() {
6208
6222
  });
6209
6223
  continue;
6210
6224
  }
6211
- const shipRef = doc11(session.fb.db, COLLECTIONS.ships, shipId);
6212
- const mirrorSnap = await getDoc10(doc11(shipRef, COLLECTIONS.runners, session.runnerId));
6225
+ const shipRef = doc10(session.fb.db, COLLECTIONS.ships, shipId);
6226
+ const mirrorSnap = await getDoc9(doc10(shipRef, COLLECTIONS.runners, session.runnerId));
6213
6227
  const mirror = mirrorSnap.data();
6214
6228
  let queued = 0;
6215
6229
  try {
@@ -6220,7 +6234,7 @@ async function runStatus() {
6220
6234
  } catch {
6221
6235
  queued = 0;
6222
6236
  }
6223
- const usageSnap = await getDoc10(doc11(shipRef, COLLECTIONS.usageDaily, utcDay(Date.now())));
6237
+ const usageSnap = await getDoc9(doc10(shipRef, COLLECTIONS.usageDaily, utcDay(Date.now())));
6224
6238
  const today = { ...EMPTY_USAGE_TOTALS, ...usageSnap.data()?.totals ?? {} };
6225
6239
  const now = Date.now();
6226
6240
  const limitsSnap = await getDocs8(collection9(shipRef, COLLECTIONS.engineLimits));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumi.ai/runner",
3
- "version": "0.15.17",
3
+ "version": "0.15.19",
4
4
  "type": "module",
5
5
  "description": "Lumi Crew runner daemon — claims jobs from your Ships and executes them as headless Claude sessions on your own machine.",
6
6
  "//name": "The ONLY package in this monorepo published to the public registry, so it is the one that does not follow the internal @lumi/crew-* convention: `@lumi` is not a scope we own, `@lumi.ai` is (the npm org). The workspace DIRECTORY stays packages/crew/runner — renaming the package is not renaming the folder.",