@agentvault/grok-bridge 0.4.1 → 0.4.3

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/bridge.d.ts CHANGED
@@ -9,9 +9,6 @@
9
9
  * Those are Claude-shaped; Grok's gate is the pre_tool_use hook and arrives with
10
10
  * the tools-on posture.
11
11
  */
12
- /** `RoomSession` is declared ONCE in this package, by grok-session.ts. The Claude
13
- * bridge declares its own in its bridge.ts; that declaration was NOT copied.
14
- * Type-only, so this cannot create a runtime import cycle. */
15
12
  import type { RoomSession } from "./grok-session.js";
16
13
  export interface RoomMessage {
17
14
  roomId: string;
@@ -167,6 +164,12 @@ export declare function attachLifecycle(channel: LifecycleChannel, opts?: {
167
164
  */
168
165
  export declare function roomTurnPrefix(roomId: string, senderName: string, roomName?: string): string;
169
166
  export declare function wireBridge(channel: RoomChannel, session: RoomSession, target: ActiveTarget, opts?: {
167
+ /**
168
+ * #1107: this agent's own name, used to recognise its own @mention. Threaded
169
+ * from the bridge config. Without it the agent can only ever answer @all —
170
+ * `_makeOwesReply` warns once, loudly, rather than going quietly deaf.
171
+ */
172
+ agentName?: string;
170
173
  roomFilter?: string;
171
174
  log?: (msg: string) => void;
172
175
  }): void;
package/dist/index.js CHANGED
@@ -1074,6 +1074,28 @@ var __toESM = (mod4, isNodeMode, target) => (target = mod4 != null ? __create(__
1074
1074
  isNodeMode || !mod4 || !mod4.__esModule ? __defProp2(target, "default", { value: mod4, enumerable: true }) : target,
1075
1075
  mod4
1076
1076
  ));
1077
+ function stripWrappers(s2) {
1078
+ let start = 0;
1079
+ let end = s2.length;
1080
+ while (start < end && WRAPPERS.includes(s2[start])) start++;
1081
+ while (end > start && WRAPPERS.includes(s2[end - 1])) end--;
1082
+ return s2.slice(start, end);
1083
+ }
1084
+ function isAbstention(text) {
1085
+ if (typeof text !== "string") return false;
1086
+ return stripWrappers(text).toLowerCase().startsWith(SENTINEL);
1087
+ }
1088
+ var SENTINEL;
1089
+ var WRAPPERS;
1090
+ var INSTRUCTION;
1091
+ var init_abstention = __esm2({
1092
+ "src/abstention.ts"() {
1093
+ "use strict";
1094
+ SENTINEL = "[[no-reply]]";
1095
+ WRAPPERS = " \r\n`\"'*_";
1096
+ INSTRUCTION = "not every message needs an answer from you: if this one is not addressed to you and asks nothing of you, reply with exactly " + SENTINEL + " and nothing else \u2014 that is discarded, not posted, and is how you stay quiet; never post a message SAYING that you are staying quiet";
1097
+ }
1098
+ });
1077
1099
  function toEpoch(v2) {
1078
1100
  if (typeof v2 === "bigint") return v2;
1079
1101
  if (typeof v2 === "number") {
@@ -63752,6 +63774,39 @@ var init_mls_group = __esm2({
63752
63774
  const s2 = this._requireState();
63753
63775
  return s2.groupContext.groupId;
63754
63776
  }
63777
+ /**
63778
+ * Proof that this client actually holds the group's keys at its current
63779
+ * epoch. Throws if not initialized.
63780
+ *
63781
+ * RFC 9420 §8.2's `epoch_authenticator`, derived from the epoch secret. Two
63782
+ * properties make it the thing this codebase has been missing:
63783
+ *
63784
+ * * every member at the same epoch computes the SAME value, so it is
63785
+ * comparable across clients and can be checked by a server;
63786
+ * * it cannot be computed without the key schedule, so possessing it is
63787
+ * evidence of membership rather than a claim about it. RFC 9420 names
63788
+ * this as its purpose: "to authenticate the shared group state".
63789
+ *
63790
+ * It is safe to publish. It is a KDF output over the epoch secret, not the
63791
+ * secret, and the RFC intends it to be shown to out-of-band verifiers.
63792
+ *
63793
+ * WHY THIS EXISTS. `mls_groups.member_device_ids` records membership; the
63794
+ * ability to decrypt lives only in one browser's localStorage. Nothing
63795
+ * compared the two, and nothing could — so a device in the roster holding no
63796
+ * keys was indistinguishable from a healthy one, on every signal the server
63797
+ * has. Measured on production 2026-08-26: one owner device carried the keys
63798
+ * for 19 of the 21 owner leaves on the platform, and one 1:1 group had been
63799
+ * unreadable by its owner's browser for two months while every status column
63800
+ * read healthy.
63801
+ *
63802
+ * `epoch` alone cannot do this job: it is a number the client reports about
63803
+ * itself, it collides across groups, and a client that has lost its state
63804
+ * still remembers one.
63805
+ */
63806
+ get epochAuthenticator() {
63807
+ const s2 = this._requireState();
63808
+ return s2.keySchedule.epochAuthenticator;
63809
+ }
63755
63810
  /** The cipher suite name in use. */
63756
63811
  get ciphersuiteName() {
63757
63812
  return this._ciphersuiteName;
@@ -63902,11 +63957,84 @@ var init_mls_group = __esm2({
63902
63957
  return { commit: commitBytes, welcome: welcomeBytes };
63903
63958
  }
63904
63959
  /**
63905
- * Remove a member from the group by leaf index.
63960
+ * Admit N members with TWO commits total, replacing any stale leaf they hold.
63961
+ *
63962
+ * `addMembers` cannot do this alone: MLS rejects an Add for an identity
63963
+ * already in the tree, so a member that lost its local state must be removed
63964
+ * first. Doing that pair PER MEMBER is what the fulfiller did, and it is not
63965
+ * merely slow — it is self-defeating.
63966
+ *
63967
+ * Live 2026-08-28 14:02:21-28Z: the owner sweep re-admitted 7 room members
63968
+ * sequentially, each as its own removeMember + addMembers. Room 007 ran
63969
+ * **epoch 71 -> 85 in seven seconds**, and the Welcome minted for member n
63970
+ * named an epoch that member n+1's commit had already replaced — so six of
63971
+ * seven were stale before they finished joining, and one (`smartcode`) could
63972
+ * not replay forward afterwards at all.
63973
+ *
63974
+ * 🎯 A REPAIR THAT MOVES THE EPOCH ONCE PER MEMBER IS RACING ITSELF.
63975
+ *
63976
+ * ⚠️ **Why two commits and not one.** RFC 9420 §12.3 allows Remove and Add in
63977
+ * a single commit, and ts-mls builds it happily — but with MORE THAN ONE of
63978
+ * each, a REMAINING member fails to process it:
63906
63979
  *
63907
- * @param memberIndex - The leaf index of the member to remove.
63908
- * @returns The commit message to broadcast.
63980
+ * OpenError at applyUpdatePathSecret (ts-mls/src/createCommit.ts:471)
63981
+ * at updatePrivateKeyPath (processMessages.ts:397)
63982
+ *
63983
+ * One remove + one add in a single commit is fine; 2+2 is not. That is
63984
+ * pinned by a test in `mls-group.test.ts` so this cannot be "optimised" back
63985
+ * into one commit without the failure showing up first. Two commits still
63986
+ * turns 7 members from 14 epochs into 2, and every joiner lands current.
63987
+ *
63988
+ * @param keyPackages - Fresh KeyPackages, one per member to admit. Members
63989
+ * already holding a leaf are removed by the first commit; members with no
63990
+ * leaf are plain adds. Duplicate identities keep the first KeyPackage.
63991
+ * @returns Every commit to broadcast IN ORDER, and the Welcome to deliver to
63992
+ * EVERY member in the batch (each finds its own secrets inside it).
63909
63993
  */
63994
+ async reAdmitMembers(keyPackages) {
63995
+ this._requireState();
63996
+ await this._ensureCiphersuite();
63997
+ const seenIdentities = [];
63998
+ const toAdd = [];
63999
+ const leavesToRemove = [];
64000
+ for (const kp of keyPackages) {
64001
+ const identity = keyPackageIdentity(kp);
64002
+ if (identity !== null) {
64003
+ if (seenIdentities.some((id) => bytesEqual(id, identity)))
64004
+ continue;
64005
+ seenIdentities.push(identity);
64006
+ const leaf = this.memberLeafIndex(identity);
64007
+ if (leaf !== null)
64008
+ leavesToRemove.push(leaf);
64009
+ }
64010
+ toAdd.push(kp);
64011
+ }
64012
+ if (toAdd.length === 0) {
64013
+ throw new Error("reAdmitMembers: no members to admit");
64014
+ }
64015
+ const commits = [];
64016
+ if (leavesToRemove.length > 0) {
64017
+ const { commit } = await this.removeMembers(leavesToRemove);
64018
+ commits.push({ commit, epoch: this.epoch });
64019
+ }
64020
+ const added = await this.addMembers(toAdd);
64021
+ commits.push({ commit: added.commit, epoch: this.epoch });
64022
+ return { commits, welcome: added.welcome };
64023
+ }
64024
+ async removeMembers(memberIndices) {
64025
+ const s2 = this._requireState();
64026
+ const cs = await this._ensureCiphersuite();
64027
+ if (memberIndices.length === 0) {
64028
+ throw new Error("removeMembers: no members to remove");
64029
+ }
64030
+ const extraProposals = memberIndices.map((removed) => ({
64031
+ proposalType: "remove",
64032
+ remove: { removed }
64033
+ }));
64034
+ const result = await createCommit({ state: s2, cipherSuite: cs, pskIndex: this._pskIndex }, { extraProposals });
64035
+ this.state = result.newState;
64036
+ return { commit: encodeMlsMessage(result.commit) };
64037
+ }
63910
64038
  async removeMember(memberIndex) {
63911
64039
  const s2 = this._requireState();
63912
64040
  const cs = await this._ensureCiphersuite();
@@ -64461,17 +64589,28 @@ var init_mls_kp_pool = __esm2({
64461
64589
  }
64462
64590
  });
64463
64591
  function ownIdentity() {
64464
- const v2 = true ? "0.23.27" : FALLBACK;
64592
+ const v2 = true ? "0.23.30" : FALLBACK;
64465
64593
  return `${PACKAGE}@${v2}`;
64466
64594
  }
64595
+ function buildSha() {
64596
+ const raw = true ? "1fff5d6" : "";
64597
+ const cleaned = (raw ?? "").trim();
64598
+ return !cleaned || cleaned === "unknown" ? null : cleaned;
64599
+ }
64600
+ function withBuildSha(identity) {
64601
+ const sha = buildSha();
64602
+ if (!sha || identity.includes("+")) return identity;
64603
+ const stamped = `${identity}+${sha}`;
64604
+ return stamped.length <= MAX_LEN ? stamped : identity;
64605
+ }
64467
64606
  function buildClientVersion(override) {
64468
64607
  try {
64469
64608
  const candidate = (override ?? "").trim();
64470
64609
  if (candidate) {
64471
- const cleaned = candidate.replace(DISALLOWED, "").slice(0, MAX_LEN);
64610
+ const cleaned = withBuildSha(candidate.replace(DISALLOWED, "").slice(0, MAX_LEN));
64472
64611
  if (cleaned && !cleaned.includes("..")) return cleaned;
64473
64612
  }
64474
- return ownIdentity().replace(DISALLOWED, "").slice(0, MAX_LEN);
64613
+ return withBuildSha(ownIdentity().replace(DISALLOWED, "").slice(0, MAX_LEN));
64475
64614
  } catch {
64476
64615
  return FALLBACK;
64477
64616
  }
@@ -65373,6 +65512,7 @@ var SecureChannel;
65373
65512
  var init_channel = __esm2({
65374
65513
  async "src/channel.ts"() {
65375
65514
  "use strict";
65515
+ init_abstention();
65376
65516
  init_mls_commit_classify();
65377
65517
  await init_libsodium_wrappers();
65378
65518
  await init_dist();
@@ -65802,6 +65942,18 @@ var init_channel = __esm2({
65802
65942
  get ownHubAddress() {
65803
65943
  return this._persisted?.hubAddress ?? "";
65804
65944
  }
65945
+ /** This agent's AgentVault hub_id. "" until enrolment persists it.
65946
+ *
65947
+ * Lives in the credential store, NOT in the channel config — which is where
65948
+ * the memory-recall helper was reading it from. `channel.config.hubId` is
65949
+ * unset for every agent enrolled through the normal flow, so
65950
+ * `channel.config.hubId || ""` produced `/api/v1/agents//memory/search`, a
65951
+ * 404 on every inbound message, swallowed by `if (!res.ok) return ""`.
65952
+ * Memory recall was silently dead while the hub_id sat right here.
65953
+ */
65954
+ get ownHubId() {
65955
+ return this._persisted?.hubId ?? "";
65956
+ }
65805
65957
  /** Public best-effort trigger to (re)resolve this agent's display_name.
65806
65958
  * Called lazily from the 1:1 DM path when the name is still unknown, so a
65807
65959
  * resolve that failed at connect (or a fresh agent that hadn't registered
@@ -66190,6 +66342,7 @@ var init_channel = __esm2({
66190
66342
  * Each session gets the same plaintext encrypted independently.
66191
66343
  */
66192
66344
  async send(plaintext, options) {
66345
+ if (isAbstention(plaintext)) return;
66193
66346
  if (this._state === "error" || this._state === "idle") {
66194
66347
  throw new Error("Channel is not ready");
66195
66348
  }
@@ -66518,7 +66671,7 @@ var init_channel = __esm2({
66518
66671
  */
66519
66672
  sendActivitySpan(spanData) {
66520
66673
  if (!this._ws || this._ws.readyState !== WebSocket.OPEN) return;
66521
- const pluginVersion = true ? "0.23.27" : "0.0.0-dev";
66674
+ const pluginVersion = true ? "0.23.30" : "0.0.0-dev";
66522
66675
  const agentName = this.config.agentName ?? "Agent";
66523
66676
  const resource = {
66524
66677
  "service.name": "agentvault-agent",
@@ -66712,6 +66865,7 @@ var init_channel = __esm2({
66712
66865
  * Uses MLS (preferred) or pairwise fan-out as fallback.
66713
66866
  */
66714
66867
  async sendToRoom(roomId, plaintext, opts) {
66868
+ if (isAbstention(plaintext)) return;
66715
66869
  if (!this._persisted?.rooms?.[roomId]) {
66716
66870
  throw new Error(`Room ${roomId} not found`);
66717
66871
  }
@@ -68455,7 +68609,7 @@ var init_channel = __esm2({
68455
68609
  agentVersion: this.config.agentVersion ?? "0.0.0",
68456
68610
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
68457
68611
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
68458
- pluginVersion: true ? "0.23.27" : "0.0.0-dev"
68612
+ pluginVersion: true ? "0.23.30" : "0.0.0-dev"
68459
68613
  });
68460
68614
  this._telemetryReporter.startAutoFlush(3e4);
68461
68615
  }
@@ -68779,7 +68933,7 @@ var init_channel = __esm2({
68779
68933
  agentVersion: this.config.agentVersion ?? "0.0.0",
68780
68934
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
68781
68935
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
68782
- pluginVersion: true ? "0.23.27" : "0.0.0-dev"
68936
+ pluginVersion: true ? "0.23.30" : "0.0.0-dev"
68783
68937
  });
68784
68938
  this._telemetryReporter.startAutoFlush(3e4);
68785
68939
  }
@@ -72923,6 +73077,58 @@ var init_gateway_send = __esm2({
72923
73077
  init_openclaw_compat();
72924
73078
  }
72925
73079
  });
73080
+ function parseMentions(text) {
73081
+ const mentions = [];
73082
+ const re2 = new RegExp(MENTION_RE.source, MENTION_RE.flags);
73083
+ let match;
73084
+ while ((match = re2.exec(text)) !== null) {
73085
+ mentions.push(match[1].toLowerCase());
73086
+ }
73087
+ return mentions;
73088
+ }
73089
+ function resolveOwn(mention, names) {
73090
+ if (names.has(mention)) return mention;
73091
+ const parts = mention.split("-");
73092
+ for (let i2 = parts.length - 1; i2 > 0; i2--) {
73093
+ const candidate = parts.slice(0, i2).join("-");
73094
+ if (names.has(candidate)) return candidate;
73095
+ }
73096
+ return null;
73097
+ }
73098
+ function owesRoomReply(plaintext, agentName, accountId, senderIsAgent, roomDisplayName, ownDisplayName, selfResolved = true) {
73099
+ const mentions = parseMentions(plaintext);
73100
+ if (mentions.length === 0) return false;
73101
+ const ownNames = ownRoomNames(agentName, accountId, roomDisplayName, ownDisplayName);
73102
+ if (mentions.some((m2) => resolveOwn(m2, ownNames) !== null)) return true;
73103
+ if (mentions.some((m2) => resolveOwn(m2, BROADCAST_NAMES) !== null)) {
73104
+ return senderIsAgent !== true;
73105
+ }
73106
+ if (!selfResolved) return true;
73107
+ return false;
73108
+ }
73109
+ function ownRoomNames(agentName, accountId, roomDisplayName, ownDisplayName) {
73110
+ const names = /* @__PURE__ */ new Set();
73111
+ const add4 = (n2) => {
73112
+ if (!n2) return;
73113
+ const lower = n2.toLowerCase();
73114
+ names.add(lower);
73115
+ names.add(lower.split(/[\s(]/)[0]);
73116
+ };
73117
+ add4(agentName);
73118
+ add4(accountId);
73119
+ add4(roomDisplayName);
73120
+ add4(ownDisplayName);
73121
+ return names;
73122
+ }
73123
+ var MENTION_RE;
73124
+ var BROADCAST_NAMES;
73125
+ var init_room_protocol = __esm2({
73126
+ "src/room-protocol.ts"() {
73127
+ "use strict";
73128
+ MENTION_RE = /@([\w-]+)/gi;
73129
+ BROADCAST_NAMES = /* @__PURE__ */ new Set(["all", "everyone"]);
73130
+ }
73131
+ });
72926
73132
  var init_llm_response_parser = __esm2({
72927
73133
  "src/llm-response-parser.ts"() {
72928
73134
  "use strict";
@@ -72956,6 +73162,7 @@ var isUsingManagedRoutes;
72956
73162
  var init_openclaw_entry = __esm2({
72957
73163
  "src/openclaw-entry.ts"() {
72958
73164
  "use strict";
73165
+ init_room_protocol();
72959
73166
  init_account_config();
72960
73167
  init_fetch_interceptor();
72961
73168
  init_http_handlers();
@@ -99014,7 +99221,8 @@ var init_index = __esm2({
99014
99221
  init_skill_invoker();
99015
99222
  await init_skill_telemetry();
99016
99223
  await init_policy_enforcer();
99017
- VERSION = true ? "0.23.27" : "0.0.0-dev";
99224
+ init_room_protocol();
99225
+ VERSION = true ? "0.23.30" : "0.0.0-dev";
99018
99226
  }
99019
99227
  });
99020
99228
  await init_index();
@@ -99767,9 +99975,32 @@ function roomTurnPrefix(roomId, senderName, roomName) {
99767
99975
  const room = roomName ? `"${roomName}"` : roomId.slice(0, 8);
99768
99976
  return `[room ${room} | ${senderName}]`;
99769
99977
  }
99978
+ function _makeOwesReply(agentName, channel, log) {
99979
+ let warnedNoIdentity = false;
99980
+ return (e) => {
99981
+ const own = agentName ?? "";
99982
+ const hub = channel.ownDisplayName ?? "";
99983
+ if (!own && !hub && !warnedNoIdentity) {
99984
+ warnedNoIdentity = true;
99985
+ log(
99986
+ "WARNING: this bridge has no agentName and no hub display_name, so it cannot recognise its own @mention (#1107). It will answer @all and any @-addressed message (fail-open), but targeted replies will be unreliable. Set the agent name in the bridge config."
99987
+ );
99988
+ }
99989
+ return owesRoomReply(
99990
+ e.plaintext,
99991
+ own,
99992
+ own,
99993
+ e.senderIsAgent,
99994
+ void 0,
99995
+ hub,
99996
+ Boolean(own || hub)
99997
+ );
99998
+ };
99999
+ }
99770
100000
  function wireBridge(channel, session, target, opts = {}) {
99771
100001
  const log = opts.log ?? (() => {
99772
100002
  });
100003
+ const _owesReply = _makeOwesReply(opts.agentName, channel, log);
99773
100004
  channel.on("error", (err) => {
99774
100005
  const msg = err instanceof Error ? err.message : String(err);
99775
100006
  log(`channel error (non-fatal, not a reconnect trigger): ${msg}`);
@@ -99808,7 +100039,7 @@ function wireBridge(channel, session, target, opts = {}) {
99808
100039
  target.setRoom(e.roomId);
99809
100040
  session.push(`${roomTurnPrefix(e.roomId, e.senderName, roomNames.get(e.roomId))}: ${e.plaintext}`, target.snapshotReply(channel, log), {
99810
100041
  autoReplyOnText: false,
99811
- replyExpected: e.senderIsAgent === false
100042
+ replyExpected: _owesReply(e)
99812
100043
  });
99813
100044
  });
99814
100045
  channel.on("message", (text, metadata) => {
@@ -99917,7 +100148,7 @@ async function main() {
99917
100148
  const info = parseInfoFlags(process.argv.slice(2));
99918
100149
  if (info.showVersion || info.showHelp) {
99919
100150
  const version2 = buildClientVersion2(
99920
- true ? "0.4.1" : void 0
100151
+ true ? "0.4.3" : void 0
99921
100152
  );
99922
100153
  if (info.showVersion) printLine(version2);
99923
100154
  if (info.showHelp) {
@@ -99970,7 +100201,7 @@ async function main() {
99970
100201
  agentName: cfg.agentName,
99971
100202
  platform: "node",
99972
100203
  clientVersion: buildClientVersion2(
99973
- true ? "0.4.1" : void 0
100204
+ true ? "0.4.3" : void 0
99974
100205
  )
99975
100206
  });
99976
100207
  channelRef = channel;
@@ -100050,6 +100281,9 @@ async function main() {
100050
100281
  const target = new ActiveTarget();
100051
100282
  wireBridge(channel, session, target, {
100052
100283
  roomFilter: cfg.roomFilter,
100284
+ // #1107: needed to recognise this agent's own @mention. Without it the
100285
+ // agent can only answer @all.
100286
+ agentName: cfg.agentName,
100053
100287
  log: (m2) => logLine("[grok-bridge] " + m2)
100054
100288
  });
100055
100289
  attachLifecycle2(channel, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentvault/grok-bridge",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "type": "module",
5
5
  "description": "AgentVault Grok Bridge — daemon bridging a Grok agent into E2E-encrypted AgentVault direct messages and rooms.",
6
6
  "main": "dist/index.js",
@@ -1,57 +0,0 @@
1
- import { type BridgeConfig } from "../config.js";
2
- import type { ReapResult } from "../reap.js";
3
- import { type ServiceBackend } from "./backend.js";
4
- export interface InstallOnReadyDeps {
5
- entrypoint: string;
6
- env: NodeJS.ProcessEnv;
7
- backend?: ServiceBackend;
8
- hasCreds?: (dataDir: string) => boolean;
9
- /** Contents of the installed launcher, or undefined when none is installed. */
10
- readLauncher?: (path: string) => string | undefined;
11
- fenceAuthExists?: (fenceHome: string) => boolean;
12
- /** Resolves when no turn is in flight (GrokSession.idle). See rule 2. */
13
- waitForIdle?: () => Promise<void>;
14
- /** Bound on that wait. The terminal coming back is the product promise, so a
15
- * wedged turn must never hold the customer's shell open. */
16
- idleTimeoutMs?: number;
17
- /** The ONE teardown (src/reap.ts). Must complete before launchctl runs. */
18
- reap: () => Promise<ReapResult>;
19
- exit: (code: number) => void;
20
- log: (msg: string) => void;
21
- }
22
- interface ReadyChannel {
23
- on(ev: "ready", cb: () => void): unknown;
24
- }
25
- /**
26
- * One-paste install: enrol in the foreground, hand off to the supervisor, exit —
27
- * so the customer's terminal comes back and the agent survives a reboot.
28
- *
29
- * Four rules this module exists to enforce.
30
- *
31
- * 1. REFUSE BEFORE REAPING. Everything that can say no does so before anything is
32
- * torn down. Refusing afterwards leaves the customer with an approved device,
33
- * a spent invite token, and no running process — strictly worse than never
34
- * having guarded at all.
35
- *
36
- * 2. REAP COMPLETELY, THEN BOOTSTRAP. `LaunchdBackend.install` runs bootout →
37
- * poll-until-gone → bootstrap → kickstart synchronously, so the service is
38
- * live the instant install returns. A partial teardown means two ACP sessions
39
- * and two WebSockets on one device. `reap()` reports failure rather than
40
- * swallowing it, and a failed reap is a refusal.
41
- *
42
- * 3. COMPARE, DON'T DETECT. `status().loaded` means only that a plist with this
43
- * label exists. The old `install` subcommand never guarded its entrypoint
44
- * (subcommand.ts), so a customer can hold a LOADED plist baked against a
45
- * pruned npx cache; restarting that repairs nothing and reports success.
46
- * Restart only when a service is loaded AND its baked entrypoint is the one we
47
- * would install. Compare the ENTRYPOINT, never the whole launcher script —
48
- * that embeds the install-time PATH and is volatile across shells.
49
- *
50
- * 4. VERIFY BEFORE CLAIMING. `backend.install` returns void, and launchd's
51
- * implementation runs `kickstart` regardless of every `bootstrap` exit code.
52
- * This log line is the customer's only feedback, so it comes after a
53
- * `status()` that returned loaded:true.
54
- */
55
- export declare function installOnReady(channel: ReadyChannel, cfg: BridgeConfig, deps: InstallOnReadyDeps): void;
56
- export {};
57
- //# sourceMappingURL=install-on-ready.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"install-on-ready.d.ts","sourceRoot":"","sources":["../../src/service/install-on-ready.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4C,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAC3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAElE,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,+EAA+E;IAC/E,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACpD,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC;iEAC6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IAChC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5B;AAMD,UAAU,YAAY;IACpB,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,YAAY,EACrB,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,kBAAkB,GACvB,IAAI,CAgHN"}