@pinet/slack-bridge 0.2.2 → 0.2.6

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 (93) hide show
  1. package/README.md +316 -451
  2. package/dist/activity-log.d.ts +10 -3
  3. package/dist/agent-event-runtime.d.ts +0 -2
  4. package/dist/agent-event-runtime.js +0 -1
  5. package/dist/agent-prompt-guidance.d.ts +1 -6
  6. package/dist/agent-prompt-guidance.js +1 -6
  7. package/dist/broker/adapters/slack.d.ts +4 -1
  8. package/dist/broker/adapters/slack.js +16 -9
  9. package/dist/broker/client.d.ts +21 -4
  10. package/dist/broker/client.js +65 -25
  11. package/dist/broker/hibernation-activation-authority.d.ts +22 -0
  12. package/dist/broker/hibernation-activation-authority.js +51 -0
  13. package/dist/broker/hibernation-activation.d.ts +75 -0
  14. package/dist/broker/hibernation-activation.js +95 -0
  15. package/dist/broker/hibernation-command-router.d.ts +54 -0
  16. package/dist/broker/hibernation-command-router.js +124 -0
  17. package/dist/broker/hibernation-runtime-adapters.d.ts +79 -0
  18. package/dist/broker/hibernation-runtime-adapters.js +473 -0
  19. package/dist/broker/hibernation-runtime-helpers.d.ts +146 -0
  20. package/dist/broker/hibernation-runtime-helpers.js +243 -0
  21. package/dist/broker/index.d.ts +16 -1
  22. package/dist/broker/index.js +68 -10
  23. package/dist/broker/lock-conflict.d.ts +109 -0
  24. package/dist/broker/lock-conflict.js +409 -0
  25. package/dist/broker/socket-server.d.ts +26 -0
  26. package/dist/broker/socket-server.js +287 -4
  27. package/dist/broker-delivery.d.ts +1 -0
  28. package/dist/broker-delivery.js +10 -1
  29. package/dist/broker-runtime.d.ts +14 -2
  30. package/dist/broker-runtime.js +114 -3
  31. package/dist/canvases.d.ts +12 -2
  32. package/dist/canvases.js +1 -3
  33. package/dist/compaction-gate.d.ts +7 -0
  34. package/dist/compaction-gate.js +35 -0
  35. package/dist/deploy-manifest.d.ts +8 -2
  36. package/dist/deploy-manifest.js +9 -7
  37. package/dist/follower-runtime.d.ts +5 -2
  38. package/dist/follower-runtime.js +39 -5
  39. package/dist/git-metadata.d.ts +5 -9
  40. package/dist/git-metadata.js +23 -42
  41. package/dist/guardrails.js +5 -0
  42. package/dist/helpers.d.ts +77 -27
  43. package/dist/helpers.js +150 -39
  44. package/dist/hibernation-config.d.ts +13 -0
  45. package/dist/hibernation-config.js +14 -0
  46. package/dist/home-tab.d.ts +13 -2
  47. package/dist/imessage-tools.d.ts +4 -1
  48. package/dist/inbox-drain-runtime.d.ts +1 -0
  49. package/dist/inbox-drain-runtime.js +5 -1
  50. package/dist/index.js +341 -108
  51. package/dist/pinet-commands.d.ts +14 -1
  52. package/dist/pinet-commands.js +93 -22
  53. package/dist/pinet-mesh-ops.d.ts +6 -4
  54. package/dist/pinet-mesh-ops.js +9 -1
  55. package/dist/pinet-runtime-composition.d.ts +2 -1
  56. package/dist/pinet-runtime-composition.js +14 -3
  57. package/dist/pinet-session-formatting.d.ts +26 -2
  58. package/dist/pinet-session-formatting.js +2 -0
  59. package/dist/pinet-tools.d.ts +14 -0
  60. package/dist/pinet-tools.js +355 -50
  61. package/dist/prompts/broker/default.md +3 -3
  62. package/dist/prompts/broker/tmux.md +3 -3
  63. package/dist/runtime-agent-context.d.ts +3 -3
  64. package/dist/runtime-agent-context.js +12 -6
  65. package/dist/runtime-mode.js +4 -0
  66. package/dist/session-ui-runtime.js +3 -1
  67. package/dist/single-player-runtime.d.ts +1 -1
  68. package/dist/single-player-runtime.js +21 -5
  69. package/dist/slack-access.d.ts +10 -2
  70. package/dist/slack-access.js +64 -22
  71. package/dist/slack-block-kit.d.ts +44 -2
  72. package/dist/slack-block-kit.js +5 -5
  73. package/dist/slack-file-access.d.ts +12 -1
  74. package/dist/slack-file-access.js +4 -4
  75. package/dist/slack-message-context.d.ts +9 -2
  76. package/dist/slack-modals.d.ts +4 -1
  77. package/dist/slack-modals.js +5 -5
  78. package/dist/slack-pinet-runtime-adapter.d.ts +1 -0
  79. package/dist/slack-pinet-runtime-adapter.js +10 -0
  80. package/dist/slack-presence.d.ts +5 -4
  81. package/dist/slack-request-runtime.d.ts +3 -2
  82. package/dist/slack-scope-diagnostics.d.ts +27 -2
  83. package/dist/slack-socket-dedup.d.ts +8 -5
  84. package/dist/slack-tools.d.ts +10 -0
  85. package/dist/slack-tools.js +51 -64
  86. package/dist/slack-upload.d.ts +17 -1
  87. package/dist/subtree-broker-runtime.d.ts +83 -2
  88. package/dist/subtree-broker-runtime.js +774 -109
  89. package/dist/tool-output-limits.d.ts +11 -0
  90. package/dist/tool-output-limits.js +20 -0
  91. package/dist/tool-registration-runtime.d.ts +3 -0
  92. package/dist/tool-registration-runtime.js +27 -0
  93. package/package.json +8 -8
@@ -3,6 +3,7 @@ import { type FollowerRuntimeDiagnostic, type SlackBridgeSettings } from "./help
3
3
  import { type LoggedActivityLogEntry } from "./activity-log.js";
4
4
  import { type SlackScopeDiagnostics } from "./slack-scope-diagnostics.js";
5
5
  import type { SlackBridgeRuntimeMode } from "./runtime-mode.js";
6
+ import type { BrokerLockInspection, BrokerSocketProbeResult, ReplaceBrokerOwnerResult } from "./broker/index.js";
6
7
  import type { RalphSnoozeStatus } from "./ralph-loop.js";
7
8
  import type { SubtreeBrokerStatus, SubtreeSpawnInput, SubtreeSpawnResult } from "./subtree-broker-runtime.js";
8
9
  export interface PinetCommandsDeps {
@@ -45,7 +46,9 @@ export interface PinetCommandsDeps {
45
46
  lastBrokerControlPlaneHomeTabRefreshAt: () => string | null;
46
47
  lastBrokerControlPlaneHomeTabError: () => string | null;
47
48
  subtreeBrokerStatus: () => SubtreeBrokerStatus;
49
+ inspectGlobalBroker: () => Promise<GlobalBrokerInspection>;
48
50
  getPinetRegistrationBlockReason: () => string;
51
+ replaceGlobalBroker: (ctx: ExtensionContext) => Promise<ReplaceBrokerOwnerResult>;
49
52
  connectAsBroker: (ctx: ExtensionContext) => Promise<void>;
50
53
  connectAsFollower: (ctx: ExtensionContext) => Promise<void>;
51
54
  reloadPinetRuntime: (ctx: ExtensionContext) => Promise<void>;
@@ -65,7 +68,7 @@ export interface PinetCommandsDeps {
65
68
  queuedInboxCount: number;
66
69
  drainedQueuedInbox: boolean;
67
70
  }>;
68
- applyLocalAgentIdentity: (name: string, emoji: string, personality: string | null) => void;
71
+ applyLocalAgentIdentity: (name: string, emoji: string, personality: string | null) => Promise<void>;
69
72
  setExtStatus: (ctx: ExtensionContext, state: "ok" | "reconnecting" | "error" | "off") => void;
70
73
  setExtCtx: (ctx: ExtensionContext) => void;
71
74
  }
@@ -73,3 +76,13 @@ export type PinetCommandAction = "start" | "follow" | "unfollow" | "reload" | "e
73
76
  export declare function formatPinetCommandHelp(): string;
74
77
  export declare function runPinetCommandAction(deps: PinetCommandsDeps, action: PinetCommandAction, args: string, ctx: ExtensionContext, usageCommand?: string): Promise<void>;
75
78
  export declare function registerPinetCommands(pi: ExtensionAPI, deps: PinetCommandsDeps): void;
79
+ export interface GlobalBrokerInspection {
80
+ lock: BrokerLockInspection;
81
+ probe: BrokerSocketProbeResult | null;
82
+ }
83
+ /**
84
+ * Machine-wide broker state line for `/pinet status`, so a disconnected
85
+ * session — precisely the one that needs recovery — can see who holds the
86
+ * broker lock and whether that broker is healthy (issue #951).
87
+ */
88
+ export declare function formatGlobalBrokerReport(report: GlobalBrokerInspection): string;
@@ -4,7 +4,11 @@ import { formatRecentActivityLogEntries } from "./activity-log.js";
4
4
  import { formatRuntimeGuardrailsPosture } from "./guardrails.js";
5
5
  import { formatSlackScopeDiagnosticsStatus, } from "./slack-scope-diagnostics.js";
6
6
  const PINET_PRIMARY_COMMANDS = [
7
- { action: "start", args: "", description: "Start as the mesh broker" },
7
+ {
8
+ action: "start",
9
+ args: "[replace]",
10
+ description: "Start as the mesh broker (replace: take over a stale/stranded broker)",
11
+ },
8
12
  { action: "follow", args: "", description: "Connect as a follower worker" },
9
13
  { action: "unfollow", args: "", description: "Disconnect from the broker" },
10
14
  { action: "reload", args: "<agent>", description: "Ask another agent to reload" },
@@ -23,7 +27,7 @@ const PINET_SECONDARY_COMMANDS = [
23
27
  { action: "rename", args: "[name]", description: "Rename this Pinet agent" },
24
28
  ];
25
29
  // ─── Registration ────────────────────────────────────────
26
- function abortCurrentTurnBeforeBrokerReload(ctx) {
30
+ function abortCurrentTurnBeforeRuntimeReload(ctx) {
27
31
  if (ctx.isIdle?.() ?? true) {
28
32
  return;
29
33
  }
@@ -106,7 +110,7 @@ function normalizePinetCommandAction(rawAction) {
106
110
  export async function runPinetCommandAction(deps, action, args, ctx, usageCommand = `/pinet ${action}`) {
107
111
  switch (action) {
108
112
  case "start":
109
- await runPinetStart(deps, ctx);
113
+ await runPinetStart(deps, args, ctx);
110
114
  return;
111
115
  case "follow":
112
116
  await runPinetFollow(deps, ctx);
@@ -124,14 +128,23 @@ export async function runPinetCommandAction(deps, action, args, ctx, usageComman
124
128
  await runPinetFree(deps, ctx);
125
129
  return;
126
130
  case "status":
127
- runPinetStatus(deps, ctx);
131
+ await runPinetStatus(deps, ctx);
128
132
  return;
129
133
  case "logs":
130
134
  runPinetLogs(deps, ctx);
131
135
  return;
132
- case "rename":
133
- runPinetRename(deps, args, ctx);
136
+ case "rename": {
137
+ const newName = args.trim();
138
+ if (newName) {
139
+ await deps.applyLocalAgentIdentity(newName, deps.agentEmoji(), deps.agentPersonality());
140
+ }
141
+ else {
142
+ const identity = generateAgentName(undefined, deps.runtimeMode() === "broker" ? "broker" : "worker");
143
+ await deps.applyLocalAgentIdentity(identity.name, identity.emoji, deps.agentPersonality());
144
+ }
145
+ ctx.ui.notify(`${deps.agentEmoji()} Agent renamed to: ${deps.agentName()}`, "info");
134
146
  return;
147
+ }
135
148
  case "snooze":
136
149
  runPinetSnooze(deps, args, ctx);
137
150
  return;
@@ -158,15 +171,21 @@ export function registerPinetCommands(pi, deps) {
158
171
  },
159
172
  });
160
173
  }
161
- async function runPinetStart(deps, ctx) {
174
+ async function runPinetStart(deps, args, ctx) {
162
175
  if (deps.pinetRegistrationBlocked()) {
163
176
  ctx.ui.notify(deps.getPinetRegistrationBlockReason(), "warning");
164
177
  return;
165
178
  }
166
179
  deps.setExtCtx(ctx);
180
+ const trimmedArgs = args.trim().toLowerCase();
181
+ const wantReplace = ["replace", "--replace", "takeover"].includes(trimmedArgs);
182
+ if (trimmedArgs && !wantReplace) {
183
+ ctx.ui.notify(`Unknown /pinet start option: ${args.trim()}\nUsage: /pinet start [replace]`, "warning");
184
+ return;
185
+ }
167
186
  if (deps.runtimeMode() === "broker") {
168
187
  try {
169
- abortCurrentTurnBeforeBrokerReload(ctx);
188
+ abortCurrentTurnBeforeRuntimeReload(ctx);
170
189
  ctx.ui.notify("Pinet broker already running — reloading current runtime", "info");
171
190
  await deps.reloadPinetRuntime(ctx);
172
191
  }
@@ -176,6 +195,26 @@ async function runPinetStart(deps, ctx) {
176
195
  }
177
196
  return;
178
197
  }
198
+ if (wantReplace) {
199
+ let replaceResult;
200
+ try {
201
+ replaceResult = await deps.replaceGlobalBroker(ctx);
202
+ }
203
+ catch (err) {
204
+ ctx.ui.notify(`Pinet broker replace failed: ${errorMsg(err)}`, "error");
205
+ deps.setExtStatus(ctx, "error");
206
+ return;
207
+ }
208
+ const stepsText = replaceResult.steps.length
209
+ ? `\n${replaceResult.steps.map((step) => `• ${step}`).join("\n")}`
210
+ : "";
211
+ if (replaceResult.outcome === "failed" || replaceResult.outcome === "owner-changed") {
212
+ ctx.ui.notify(`Pinet broker replace ${replaceResult.outcome === "failed" ? "failed" : "aborted"}: ${replaceResult.error ?? "unknown error"}${stepsText}`, "error");
213
+ deps.setExtStatus(ctx, "error");
214
+ return;
215
+ }
216
+ ctx.ui.notify(`Pinet broker replace: ${replaceResult.outcome}.${stepsText}`, "info");
217
+ }
179
218
  try {
180
219
  await deps.connectAsBroker(ctx);
181
220
  }
@@ -189,13 +228,20 @@ async function runPinetFollow(deps, ctx) {
189
228
  ctx.ui.notify(deps.getPinetRegistrationBlockReason(), "warning");
190
229
  return;
191
230
  }
192
- if (deps.runtimeMode() === "follower") {
231
+ if (deps.runtimeMode() === "follower" && deps.runtimeConnected()) {
193
232
  ctx.ui.notify("Pinet already running (follower)", "info");
194
233
  return;
195
234
  }
196
235
  deps.setExtCtx(ctx);
197
236
  try {
198
- await deps.connectAsFollower(ctx);
237
+ if (deps.runtimeMode() === "follower") {
238
+ abortCurrentTurnBeforeRuntimeReload(ctx);
239
+ ctx.ui.notify("Pinet follower disconnected — reconnecting...", "info");
240
+ await deps.reloadPinetRuntime(ctx);
241
+ }
242
+ else {
243
+ await deps.connectAsFollower(ctx);
244
+ }
199
245
  ctx.ui.notify(`${deps.agentEmoji()} ${deps.agentName()} — following broker`, "info");
200
246
  }
201
247
  catch (err) {
@@ -475,8 +521,43 @@ async function runPinetSubtree(deps, args, ctx) {
475
521
  ctx.ui.notify(`Subtree broker start failed: ${errorMsg(err)}`, "error");
476
522
  }
477
523
  }
478
- function runPinetStatus(deps, ctx) {
524
+ /**
525
+ * Machine-wide broker state line for `/pinet status`, so a disconnected
526
+ * session — precisely the one that needs recovery — can see who holds the
527
+ * broker lock and whether that broker is healthy (issue #951).
528
+ */
529
+ export function formatGlobalBrokerReport(report) {
530
+ const { lock, probe } = report;
531
+ switch (lock.state) {
532
+ case "none":
533
+ return "Global broker: none running on this machine (lock free). Use /pinet start.";
534
+ case "unreadable":
535
+ return "Global broker: unreadable lock file — /pinet start will reclaim it.";
536
+ case "stale-dead":
537
+ return `Global broker: stale lock from dead pid ${lock.owner.pid} — /pinet start will reclaim it.`;
538
+ case "stale-pid-reused":
539
+ return `Global broker: lock pid ${lock.owner.pid} was reused by an unrelated process — /pinet start will reclaim it.`;
540
+ case "alive": {
541
+ const since = lock.owner.createdAt ? ` since ${lock.owner.createdAt}` : "";
542
+ if (probe === "healthy") {
543
+ return `Global broker: active (pid ${lock.owner.pid}${since}). Use /pinet follow to join it, or /pinet start replace to take it over.`;
544
+ }
545
+ const socketState = probe === "unreachable" ? "unreachable" : "unresponsive";
546
+ return `Global broker: pid ${lock.owner.pid} holds the lock${since} but the broker socket is ${socketState} — likely stranded. Use /pinet start replace to recover.`;
547
+ }
548
+ }
549
+ }
550
+ async function runPinetStatus(deps, ctx) {
479
551
  const mode = deps.runtimeMode();
552
+ let globalBrokerInfo = [];
553
+ if (mode === "off" || mode === "single") {
554
+ try {
555
+ globalBrokerInfo = [formatGlobalBrokerReport(await deps.inspectGlobalBroker())];
556
+ }
557
+ catch (err) {
558
+ globalBrokerInfo = [`Global broker: inspection failed (${errorMsg(err)})`];
559
+ }
560
+ }
480
561
  const ownedCount = [...deps.threads().values()].filter((t) => agentOwnsThread(t.owner, deps.agentName(), deps.agentAliases(), deps.agentOwnerToken())).length;
481
562
  const users = deps.allowedUsers();
482
563
  const s = deps.settings();
@@ -541,6 +622,7 @@ function runPinetStatus(deps, ctx) {
541
622
  ...ralphSnoozeInfo,
542
623
  ...brokerHomeTabInfo,
543
624
  ...subtreeBrokerInfo,
625
+ ...globalBrokerInfo,
544
626
  ].join("\n"), "info");
545
627
  }
546
628
  function runPinetLogs(deps, ctx) {
@@ -551,17 +633,6 @@ function runPinetLogs(deps, ctx) {
551
633
  formatRecentActivityLogEntries(deps.recentActivityLogEntries(10)),
552
634
  ].join("\n\n"), s.logChannel ? "info" : "warning");
553
635
  }
554
- function runPinetRename(deps, args, ctx) {
555
- const newName = args.trim();
556
- if (!newName) {
557
- const fresh = generateAgentName(undefined, deps.runtimeMode() === "broker" ? "broker" : "worker");
558
- deps.applyLocalAgentIdentity(fresh.name, fresh.emoji, deps.agentPersonality());
559
- }
560
- else {
561
- deps.applyLocalAgentIdentity(newName, deps.agentEmoji(), deps.agentPersonality());
562
- }
563
- ctx.ui.notify(`${deps.agentEmoji()} Agent renamed to: ${deps.agentName()}`, "info");
564
- }
565
636
  function errorMsg(err) {
566
637
  return err instanceof Error ? err.message : String(err);
567
638
  }
@@ -1,6 +1,8 @@
1
1
  import { type AgentMessageStorage } from "./broker/agent-messaging.js";
2
2
  import type { AgentInfo, AgentSessionSearchInfo, AgentSessionSearchOptions, AgentSessionSummary, TaskAssignmentInfo, TaskAssignmentKind } from "./broker/types.js";
3
3
  import type { ActivityLogEntry } from "./activity-log.js";
4
+ export type PinetMeshOpsAgentMetadata = Record<string, unknown>;
5
+ export type PinetMeshOpsMessageMetadata = Record<string, unknown>;
4
6
  export interface PinetMeshOpsAgentRecord {
5
7
  emoji: string;
6
8
  name: string;
@@ -9,7 +11,7 @@ export interface PinetMeshOpsAgentRecord {
9
11
  stableId?: string | null;
10
12
  session?: AgentSessionSummary | null;
11
13
  status: "working" | "idle";
12
- metadata: Record<string, unknown> | null;
14
+ metadata: PinetMeshOpsAgentMetadata | null;
13
15
  lastHeartbeat: string;
14
16
  lastSeen?: string;
15
17
  disconnectedAt?: string | null;
@@ -59,7 +61,7 @@ export interface PinetMeshOpsFollowerAgentRecord extends Omit<PinetMeshOpsAgentR
59
61
  status?: PinetMeshOpsAgentRecord["status"] | null;
60
62
  }
61
63
  export interface PinetMeshOpsFollowerClientPort {
62
- sendAgentMessage: (target: string, body: string, metadata?: Record<string, unknown>) => Promise<number>;
64
+ sendAgentMessage: (target: string, body: string, metadata?: PinetMeshOpsMessageMetadata) => Promise<number>;
63
65
  scheduleWakeup: (fireAt: string, message: string) => Promise<{
64
66
  id: number;
65
67
  fireAt: string;
@@ -74,7 +76,7 @@ export interface PinetMeshOpsDeps {
74
76
  getActiveBrokerSelfId: () => string | null;
75
77
  getAgentName: () => string;
76
78
  getFollowerClient: () => PinetMeshOpsFollowerClientPort | null;
77
- sendSubtreeAgentMessage?: (target: string, body: string, metadata?: Record<string, unknown>) => Promise<{
79
+ sendSubtreeAgentMessage?: (target: string, body: string, metadata?: PinetMeshOpsMessageMetadata) => Promise<{
78
80
  messageId: number;
79
81
  target: string;
80
82
  threadId: string;
@@ -83,7 +85,7 @@ export interface PinetMeshOpsDeps {
83
85
  logActivity: (entry: ActivityLogEntry) => void;
84
86
  }
85
87
  export interface PinetMeshOps {
86
- sendPinetAgentMessage: (target: string, body: string, metadata?: Record<string, unknown>) => Promise<{
88
+ sendPinetAgentMessage: (target: string, body: string, metadata?: PinetMeshOpsMessageMetadata) => Promise<{
87
89
  messageId: number;
88
90
  target: string;
89
91
  transferredThreadId?: string;
@@ -1,6 +1,6 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
- import { normalizeOutgoingPinetControlMessage } from "./helpers.js";
3
+ import { normalizeOutgoingPinetControlMessage, normalizeOutgoingPinetSteeringMessage, } from "./helpers.js";
4
4
  import { dispatchBroadcastAgentMessage, dispatchDirectAgentMessage, isBroadcastChannelTarget, } from "./broker/agent-messaging.js";
5
5
  import { summarizePinetStableId } from "./pinet-session-formatting.js";
6
6
  import { extractTaskAssignmentsFromMessage } from "./task-assignments.js";
@@ -13,6 +13,13 @@ function prepareOutgoingPinetAgentMessage(body, metadata) {
13
13
  metadata: control.metadata,
14
14
  };
15
15
  }
16
+ const steering = normalizeOutgoingPinetSteeringMessage(body, metadata);
17
+ if (steering) {
18
+ return {
19
+ body: steering.body,
20
+ metadata: steering.metadata,
21
+ };
22
+ }
16
23
  return { body, metadata };
17
24
  }
18
25
  function getThreadOwnershipTransferMetadata(metadata) {
@@ -34,6 +41,7 @@ function appendSlackThreadTransferNotice(body, threadId, channel) {
34
41
  `- channel: ${channel}`,
35
42
  `- To report directly in the transferred Slack thread, use slack_send with thread_ts ${threadId}; the channel is already recorded in Pinet.`,
36
43
  "- If slack_send says the thread is already owned by another agent, ask the broker to inspect ownership and transfer it again.",
44
+ "- If slack_send says the Pinet broker is unavailable, wait for the broker to reconnect \u2014 do NOT retry via post_channel; direct posting would bypass thread ownership (#855).",
37
45
  ].join("\n");
38
46
  }
39
47
  export function parseGitHubRemoteRepo(remoteUrl) {
@@ -19,9 +19,10 @@ export interface ConnectPinetRuntimeAdaptersOptions {
19
19
  broker: Pick<Broker, "addAdapter">;
20
20
  bindings: PinetRuntimeAdapterBinding[];
21
21
  onInbound: (message: InboundMessage) => void;
22
+ signal?: AbortSignal;
22
23
  }
23
24
  export interface ConnectPinetRuntimeAdaptersResult {
24
25
  botUserId: string | null;
25
26
  }
26
27
  export declare function buildPinetRuntimeAdapterBindings(factories: readonly PinetRuntimeAdapterFactory[], context: PinetRuntimeAdapterFactoryContext): Promise<PinetRuntimeAdapterBinding[]>;
27
- export declare function connectPinetRuntimeAdapters({ broker, bindings, onInbound, }: ConnectPinetRuntimeAdaptersOptions): Promise<ConnectPinetRuntimeAdaptersResult>;
28
+ export declare function connectPinetRuntimeAdapters({ broker, bindings, onInbound, signal, }: ConnectPinetRuntimeAdaptersOptions): Promise<ConnectPinetRuntimeAdaptersResult>;
@@ -8,13 +8,24 @@ export async function buildPinetRuntimeAdapterBindings(factories, context) {
8
8
  }
9
9
  return bindings;
10
10
  }
11
- export async function connectPinetRuntimeAdapters({ broker, bindings, onInbound, }) {
11
+ export async function connectPinetRuntimeAdapters({ broker, bindings, onInbound, signal, }) {
12
12
  let botUserId = null;
13
13
  for (const binding of bindings) {
14
+ signal?.throwIfAborted();
14
15
  binding.adapter.onInbound(onInbound);
15
16
  broker.addAdapter(binding.adapter);
16
- await binding.adapter.connect();
17
- botUserId ??= binding.getBotUserId?.() ?? null;
17
+ const abortConnect = () => {
18
+ void binding.adapter.disconnect().catch(() => undefined);
19
+ };
20
+ signal?.addEventListener("abort", abortConnect, { once: true });
21
+ try {
22
+ await binding.adapter.connect();
23
+ signal?.throwIfAborted();
24
+ botUserId ??= binding.getBotUserId?.() ?? null;
25
+ }
26
+ finally {
27
+ signal?.removeEventListener("abort", abortConnect);
28
+ }
18
29
  }
19
30
  return { botUserId };
20
31
  }
@@ -5,9 +5,33 @@ export interface ParsedPinetStableId {
5
5
  locator: string;
6
6
  hasPath: boolean;
7
7
  }
8
+ export type PinetSessionFullDetails = AgentSessionSearchInfo & {
9
+ session: AgentSessionSummary | null;
10
+ jsonlPath?: string;
11
+ };
12
+ export interface PinetSessionCompactDetails {
13
+ agentId: string;
14
+ agentName: string;
15
+ emoji: string;
16
+ pid: number;
17
+ status: AgentSessionSearchInfo["status"];
18
+ health: "disconnected" | "live";
19
+ session: string | null;
20
+ sessionKind: AgentSessionKind | null;
21
+ host: string | null;
22
+ repo: string | null;
23
+ branch: string | null;
24
+ runtimeKind: AgentSessionSearchInfo["runtimeKind"];
25
+ runtimeLocator: string | null;
26
+ tmuxSession: string | null;
27
+ lastSeen: string;
28
+ disconnectedAt: string | null;
29
+ relatedThreadIds: string[];
30
+ matchedBy: string[];
31
+ }
8
32
  export declare function parsePinetStableId(stableId: string | null | undefined): ParsedPinetStableId | null;
9
33
  export declare function summarizePinetStableId(stableId: string | null | undefined): AgentSessionSummary | null;
10
34
  export declare function getPinetSessionPath(stableId: string | null | undefined): string | null;
11
35
  export declare function getPinetSessionFilename(stableId: string | null | undefined): string | null;
12
- export declare function buildPinetSessionFullDetails(session: AgentSessionSearchInfo): Record<string, unknown>;
13
- export declare function buildPinetSessionCompactDetails(session: AgentSessionSearchInfo): Record<string, unknown>;
36
+ export declare function buildPinetSessionFullDetails(session: AgentSessionSearchInfo): PinetSessionFullDetails;
37
+ export declare function buildPinetSessionCompactDetails(session: AgentSessionSearchInfo): PinetSessionCompactDetails;
@@ -71,6 +71,8 @@ export function buildPinetSessionCompactDetails(session) {
71
71
  host: summary?.host ?? null,
72
72
  repo: session.repo,
73
73
  branch: session.branch,
74
+ runtimeKind: session.runtimeKind,
75
+ runtimeLocator: session.runtimeLocator,
74
76
  tmuxSession: session.tmuxSession,
75
77
  lastSeen: session.lastSeen,
76
78
  disconnectedAt: session.disconnectedAt,
@@ -1,6 +1,8 @@
1
1
  import { type PinetReadOptions, type PinetReadResult } from "@pinet/pinet-core/pinet-read-formatting";
2
2
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
3
+ import { type AgentLifecycleStatus, type HibernationCommandResult } from "@pinet/broker-core";
3
4
  import type { RalphSnoozeStatus } from "./ralph-loop.js";
5
+ import { type SubtreeSpawnLaunchHandle } from "./subtree-broker-runtime.js";
4
6
  import type { AgentSessionSearchInfo, AgentSessionSearchOptions, AgentSessionSummary, PortLeaseAcquireInput, PortLeaseInfo, PortLeaseListOptions, PortLeaseReleaseInput, PortLeaseRenewInput, PinetLaneInfo, PinetLaneListOptions, PinetLaneParticipantInfo, PinetLaneParticipantUpsertInput, PinetLaneUpsertInput } from "./broker/types.js";
5
7
  export interface PinetToolsAgentRecord {
6
8
  emoji: string;
@@ -29,10 +31,12 @@ export interface PinetSubtreeSpawnInput {
29
31
  repo: string;
30
32
  role?: string;
31
33
  laneId?: string;
34
+ cleanupHandle?: SubtreeSpawnLaunchHandle;
32
35
  }
33
36
  export interface PinetSubtreeSpawnResult {
34
37
  status: "started";
35
38
  launchId: string;
39
+ runtimeKind: "tmux" | "herdr";
36
40
  sessionName: string;
37
41
  repoPath: string;
38
42
  role: string;
@@ -47,6 +51,7 @@ export interface PinetSubtreeSpawnResult {
47
51
  childLaunchEnv: Record<string, string>;
48
52
  }
49
53
  export interface RegisterPinetToolsDeps {
54
+ promptGuidelines?: string[];
50
55
  pinetEnabled: () => boolean;
51
56
  brokerRole: () => "broker" | "follower" | null;
52
57
  requireToolPolicy: (toolName: string, threadTs: string | undefined, action: string) => void;
@@ -97,6 +102,15 @@ export interface RegisterPinetToolsDeps {
97
102
  reason?: string | null;
98
103
  }) => RalphSnoozeStatus;
99
104
  clearRalphSnooze?: () => RalphSnoozeStatus;
105
+ runHibernateCommand?: (input: {
106
+ target: string;
107
+ reason?: string;
108
+ }) => Promise<HibernationCommandResult>;
109
+ runWakeCommand?: (input: {
110
+ target: string;
111
+ reason?: string;
112
+ }) => Promise<HibernationCommandResult>;
113
+ getAgentLifecycleProjection?: (agentIds?: string[]) => AgentLifecycleStatus[];
100
114
  }
101
115
  type PinetDispatcherStatus = "succeeded" | "failed";
102
116
  interface PinetRenderContentBlock {