@pinet/slack-bridge 0.2.4 → 0.2.7

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 +26 -1
  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 +25 -0
  26. package/dist/broker/socket-server.js +234 -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 +340 -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 +8 -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 +3 -1
  85. package/dist/slack-tools.js +26 -16
  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
@@ -23,10 +23,16 @@ export interface DeployResult {
23
23
  appId: string;
24
24
  scopeChanges: ManifestScopeChanges;
25
25
  }
26
- export declare function diffManifestScopes(beforeManifest: Record<string, unknown> | null | undefined, afterManifest: Record<string, unknown> | null | undefined): ManifestScopeChanges;
26
+ type SlackManifestPrimitive = string | number | boolean | null;
27
+ export type SlackManifestValue = SlackManifestPrimitive | SlackManifestObject | SlackManifestValue[];
28
+ export interface SlackManifestObject {
29
+ [key: string]: SlackManifestValue;
30
+ }
31
+ export declare function diffManifestScopes(beforeManifest: SlackManifestObject | null | undefined, afterManifest: SlackManifestObject | null | undefined): ManifestScopeChanges;
27
32
  export declare function formatScopeChangeSummary(changes: ManifestScopeChanges): string[];
28
33
  export declare function resolveDeployConfig(settings: SlackBridgeSettings, env: NodeJS.ProcessEnv, cwd?: string): ResolvedDeployConfig;
29
34
  export declare function getDeployConfigError(config: ResolvedDeployConfig): string | null;
30
- export declare function applyConfiguredSlashCommands(manifest: Record<string, unknown>, settings: SlackBridgeSettings): Record<string, unknown>;
35
+ export declare function applyConfiguredSlashCommands(manifest: SlackManifestObject, settings: SlackBridgeSettings): SlackManifestObject;
31
36
  export declare function deploySlackManifest(config: ResolvedDeployConfig): Promise<DeployResult>;
32
37
  export declare function run(): Promise<void>;
38
+ export {};
@@ -56,12 +56,14 @@ function buildSlackRequest(method, token, body) {
56
56
  },
57
57
  };
58
58
  }
59
- function asRecord(value) {
60
- return typeof value === "object" && value !== null ? value : null;
59
+ function asManifestObject(value) {
60
+ return value != null && typeof value === "object" && !Array.isArray(value)
61
+ ? value
62
+ : null;
61
63
  }
62
64
  function readScopeList(manifest, scopeType) {
63
- const oauthConfig = asRecord(manifest?.oauth_config);
64
- const scopes = asRecord(oauthConfig?.scopes);
65
+ const oauthConfig = asManifestObject(manifest?.oauth_config);
66
+ const scopes = asManifestObject(oauthConfig?.scopes);
65
67
  const raw = scopes?.[scopeType];
66
68
  if (!Array.isArray(raw)) {
67
69
  return [];
@@ -127,9 +129,9 @@ export function getDeployConfigError(config) {
127
129
  }
128
130
  export function applyConfiguredSlashCommands(manifest, settings) {
129
131
  const commandNames = resolveSlackAgentCommandNames(settings);
130
- const features = asRecord(manifest.features) ?? {};
131
- const oauthConfig = asRecord(manifest.oauth_config) ?? {};
132
- const scopes = asRecord(oauthConfig.scopes) ?? {};
132
+ const features = asManifestObject(manifest.features) ?? {};
133
+ const oauthConfig = asManifestObject(manifest.oauth_config) ?? {};
134
+ const scopes = asManifestObject(oauthConfig.scopes) ?? {};
133
135
  const botScopes = readScopeList(manifest, "bot");
134
136
  const nextBotScopes = botScopes.includes("commands") ? botScopes : [...botScopes, "commands"];
135
137
  return {
@@ -2,6 +2,7 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
  import { type FollowerRuntimeDiagnostic, type FollowerThreadState, type InboxMessage, type PinetControlCommand, type PinetRemoteControlRequestResult, type SlackBridgeSettings } from "./helpers.js";
3
3
  import { type FollowerDeliveryState } from "./follower-delivery.js";
4
4
  import { BrokerClient } from "./broker/client.js";
5
+ export declare function resolveBrokerSocketPath(): string;
5
6
  export type BrokerClientRef = {
6
7
  client: BrokerClient;
7
8
  pollInterval: ReturnType<typeof setInterval> | null;
@@ -24,12 +25,13 @@ export interface FollowerRuntimeDeps {
24
25
  getLastDmChannel: () => string | null;
25
26
  setLastDmChannel: (channelId: string | null) => void;
26
27
  pushInboxMessages: (messages: InboxMessage[]) => void;
27
- getAgentMetadata: (role: "broker" | "worker") => Promise<Record<string, unknown>>;
28
+ getAgentMetadata: (role: "broker" | "worker", signal?: AbortSignal) => Promise<Record<string, unknown>>;
28
29
  applyRegistrationIdentity: (registration: {
29
30
  name: string;
30
31
  emoji: string;
31
32
  metadata?: Record<string, unknown> | null;
32
33
  }) => void;
34
+ onRegistrationIdentityApplied: () => Promise<void>;
33
35
  persistState: () => void;
34
36
  updateBadge: () => void;
35
37
  maybeDrainInboxIfIdle: (ctx: ExtensionContext) => boolean;
@@ -37,6 +39,7 @@ export interface FollowerRuntimeDeps {
37
39
  deferControlAck: (command: PinetControlCommand, inboxId: number) => void;
38
40
  runRemoteControl: (command: PinetControlCommand, ctx: ExtensionContext) => void;
39
41
  deliverFollowUpMessage: (text: string) => boolean;
42
+ deliverSteeringMessage: (text: string, ctx: ExtensionContext) => boolean;
40
43
  setExtStatus: (ctx: ExtensionContext, state: "ok" | "reconnecting" | "error" | "off") => void;
41
44
  getRuntimeDiagnostic: () => FollowerRuntimeDiagnostic | null;
42
45
  setRuntimeDiagnostic: (diagnostic: FollowerRuntimeDiagnostic | null) => void;
@@ -45,7 +48,7 @@ export interface FollowerRuntimeDeps {
45
48
  deliveryState: FollowerDeliveryState;
46
49
  }
47
50
  export interface FollowerRuntime {
48
- connect: (ctx: ExtensionContext) => Promise<BrokerClientRef>;
51
+ connect: (ctx: ExtensionContext, signal?: AbortSignal) => Promise<BrokerClientRef>;
49
52
  disconnect: (ctx: ExtensionContext, options?: {
50
53
  releaseIdentity?: boolean;
51
54
  }) => Promise<{
@@ -1,7 +1,7 @@
1
- import { buildFollowerRuntimeDiagnostic, buildPinetOwnerToken, extractPinetControlCommand, formatPinetInboxMessages, getFollowerOwnedThreadReclaims, getFollowerReconnectUiUpdate, partitionFollowerInboxEntries, resolvePinetMeshAuth, resolveRuntimeAgentIdentity, syncFollowerInboxEntries, syncTransferredSlackThreadContexts, } from "./helpers.js";
1
+ import { buildFollowerRuntimeDiagnostic, buildPinetOwnerToken, extractPinetSteeringInboxEntry, extractPinetControlCommand, formatPinetInboxMessages, formatPinetSteeringMessage, getFollowerOwnedThreadReclaims, getFollowerReconnectUiUpdate, partitionFollowerInboxEntries, resolvePinetMeshAuth, resolveRuntimeAgentIdentity, syncFollowerInboxEntries, syncTransferredSlackThreadContexts, } from "./helpers.js";
2
2
  import { drainFollowerAckBatches, hasDeliveredFollowerInboxIds, isFollowerInboxIdTracked, markFollowerInboxIdsDelivered, queueFollowerInboxIds, resetFollowerDeliveryState, } from "./follower-delivery.js";
3
3
  import { BrokerClient, DEFAULT_SOCKET_PATH } from "./broker/client.js";
4
- function resolveBrokerSocketPath() {
4
+ export function resolveBrokerSocketPath() {
5
5
  const envPath = process.env.PINET_SOCKET_PATH?.trim();
6
6
  return envPath && envPath.length > 0 ? envPath : DEFAULT_SOCKET_PATH;
7
7
  }
@@ -91,7 +91,8 @@ export function createFollowerRuntime(deps) {
91
91
  followerStatusSyncTarget = null;
92
92
  resetFollowerDeliveryState(deps.deliveryState);
93
93
  }
94
- async function connect(ctx) {
94
+ async function connect(ctx, signal) {
95
+ signal?.throwIfAborted();
95
96
  deps.refreshSettings();
96
97
  const meshAuth = resolvePinetMeshAuth(deps.getSettings());
97
98
  const client = new BrokerClient({
@@ -99,6 +100,10 @@ export function createFollowerRuntime(deps) {
99
100
  ...(meshAuth.meshSecret ? { meshSecret: meshAuth.meshSecret } : {}),
100
101
  ...(meshAuth.meshSecretPath ? { meshSecretPath: meshAuth.meshSecretPath } : {}),
101
102
  });
103
+ const abortConnect = () => {
104
+ client.disconnect();
105
+ };
106
+ signal?.addEventListener("abort", abortConnect, { once: true });
102
107
  async function registerFollowerRuntime() {
103
108
  deps.refreshSettings();
104
109
  const settings = deps.getSettings();
@@ -107,8 +112,9 @@ export function createFollowerRuntime(deps) {
107
112
  const hasExplicitIdentityRequest = Boolean(settings.agentName?.trim() && settings.agentEmoji?.trim()) ||
108
113
  Boolean(process.env.PI_NICKNAME?.trim());
109
114
  deps.setAgentOwnerToken(buildPinetOwnerToken(deps.getAgentStableId()));
110
- const registration = await client.register(hasExplicitIdentityRequest ? workerIdentity.name : "", hasExplicitIdentityRequest ? workerIdentity.emoji : "", await deps.getAgentMetadata("worker"), deps.getAgentStableId());
115
+ const registration = await client.register(hasExplicitIdentityRequest ? workerIdentity.name : "", hasExplicitIdentityRequest ? workerIdentity.emoji : "", await deps.getAgentMetadata("worker", signal), deps.getAgentStableId());
111
116
  deps.applyRegistrationIdentity(registration);
117
+ await deps.onRegistrationIdentityApplied();
112
118
  client.setHeartbeatMetadataProvider(() => deps.getAgentMetadata("worker"));
113
119
  }
114
120
  async function resumeThreadClaims() {
@@ -179,7 +185,29 @@ export function createFollowerRuntime(deps) {
179
185
  }
180
186
  return;
181
187
  }
182
- const { nudges, agentMessages, regular } = partitionFollowerInboxEntries(remainingEntries);
188
+ const steeringEntries = [];
189
+ const queuedEntries = [];
190
+ for (const entry of remainingEntries) {
191
+ const steering = extractPinetSteeringInboxEntry(entry);
192
+ if (steering) {
193
+ steeringEntries.push(steering);
194
+ continue;
195
+ }
196
+ queuedEntries.push(entry);
197
+ }
198
+ if (steeringEntries.length > 0) {
199
+ const deliveredSteeringIds = [];
200
+ for (const entry of steeringEntries) {
201
+ if (deps.deliverSteeringMessage(formatPinetSteeringMessage(entry), ctx)) {
202
+ deliveredSteeringIds.push(entry.inboxId);
203
+ }
204
+ }
205
+ if (deliveredSteeringIds.length > 0) {
206
+ markFollowerInboxIdsDelivered(deps.deliveryState, deliveredSteeringIds);
207
+ void flushDeliveredAcks();
208
+ }
209
+ }
210
+ const { nudges, agentMessages, regular } = partitionFollowerInboxEntries(queuedEntries);
183
211
  if (nudges.length > 0) {
184
212
  const nudgeText = nudges
185
213
  .map((nudge) => nudge.message.body ?? "")
@@ -233,7 +261,9 @@ export function createFollowerRuntime(deps) {
233
261
  }
234
262
  try {
235
263
  await client.connect();
264
+ signal?.throwIfAborted();
236
265
  await registerFollowerRuntime();
266
+ signal?.throwIfAborted();
237
267
  deps.setRuntimeDiagnostic(null);
238
268
  syncedFollowerStatus = "idle";
239
269
  clientRef = {
@@ -304,6 +334,7 @@ export function createFollowerRuntime(deps) {
304
334
  void deps.handleTerminalReconnectFailure(ctx, reconnectError);
305
335
  });
306
336
  await resumeThreadClaims();
337
+ signal?.throwIfAborted();
307
338
  startPolling();
308
339
  return clientRef;
309
340
  }
@@ -315,6 +346,9 @@ export function createFollowerRuntime(deps) {
315
346
  resetFollowerRuntimeState();
316
347
  throw error;
317
348
  }
349
+ finally {
350
+ signal?.removeEventListener("abort", abortConnect);
351
+ }
318
352
  }
319
353
  async function disconnect(_ctx, options = {}) {
320
354
  const current = clientRef;
@@ -4,6 +4,8 @@ type ExecFileResult = {
4
4
  export type ExecFileAsyncLike = (file: string, args: string[], options: {
5
5
  cwd: string;
6
6
  encoding: "utf-8";
7
+ timeout: number;
8
+ signal?: AbortSignal;
7
9
  }) => Promise<ExecFileResult>;
8
10
  export interface GitContext {
9
11
  cwd: string;
@@ -23,13 +25,7 @@ export interface GitDynamicState {
23
25
  /** True when any git probe failed; callers should treat omitted values as unknown. */
24
26
  probeFailed: boolean;
25
27
  }
26
- export declare function probeGitBranch(cwd?: string, runner?: ExecFileAsyncLike): Promise<string | undefined>;
27
- export declare function probeGitDynamic(cwd?: string, runner?: ExecFileAsyncLike): Promise<GitDynamicState>;
28
- export declare function probeGitContext(cwd?: string, runner?: ExecFileAsyncLike): Promise<GitContext>;
29
- export interface GitContextCache {
30
- get(): Promise<GitContext>;
31
- peek(): GitContext | null;
32
- clear(): void;
33
- }
34
- export declare function createGitContextCache(loader: () => Promise<GitContext>): GitContextCache;
28
+ export declare function probeGitBranch(cwd?: string, runner?: ExecFileAsyncLike, signal?: AbortSignal): Promise<string | undefined>;
29
+ export declare function probeGitDynamic(cwd?: string, runner?: ExecFileAsyncLike, signal?: AbortSignal): Promise<GitDynamicState>;
30
+ export declare function probeGitContext(cwd?: string, runner?: ExecFileAsyncLike, signal?: AbortSignal): Promise<GitContext>;
35
31
  export {};
@@ -2,26 +2,34 @@ import { execFile } from "node:child_process";
2
2
  import * as path from "node:path";
3
3
  import { promisify } from "node:util";
4
4
  const execFileAsync = promisify(execFile);
5
- async function runGitCommand(args, cwd, runner) {
5
+ const GIT_PROBE_TIMEOUT_MS = 2_000;
6
+ async function runGitCommand(args, cwd, runner, signal) {
7
+ signal?.throwIfAborted();
6
8
  try {
7
- const result = await runner("git", args, { cwd, encoding: "utf-8" });
9
+ const result = await runner("git", args, {
10
+ cwd,
11
+ encoding: "utf-8",
12
+ timeout: GIT_PROBE_TIMEOUT_MS,
13
+ signal,
14
+ });
8
15
  const stdout = typeof result.stdout === "string" ? result.stdout : result.stdout?.toString();
9
16
  return { stdout, ok: true };
10
17
  }
11
18
  catch {
19
+ signal?.throwIfAborted();
12
20
  return { stdout: undefined, ok: false };
13
21
  }
14
22
  }
15
- async function readGitTrimmed(args, cwd, runner) {
16
- const { stdout, ok } = await runGitCommand(args, cwd, runner);
23
+ async function readGitTrimmed(args, cwd, runner, signal) {
24
+ const { stdout, ok } = await runGitCommand(args, cwd, runner, signal);
17
25
  const trimmed = stdout?.trim();
18
26
  return { value: trimmed ? trimmed : undefined, ok };
19
27
  }
20
- export async function probeGitBranch(cwd = process.cwd(), runner = execFileAsync) {
21
- return (await readGitTrimmed(["branch", "--show-current"], cwd, runner)).value;
28
+ export async function probeGitBranch(cwd = process.cwd(), runner = execFileAsync, signal) {
29
+ return (await readGitTrimmed(["branch", "--show-current"], cwd, runner, signal)).value;
22
30
  }
23
- async function probeGitDirty(cwd, runner) {
24
- const { stdout, ok } = await runGitCommand(["status", "--porcelain", "--untracked-files=normal"], cwd, runner);
31
+ async function probeGitDirty(cwd, runner, signal) {
32
+ const { stdout, ok } = await runGitCommand(["status", "--porcelain", "--untracked-files=normal"], cwd, runner, signal);
25
33
  if (!ok) {
26
34
  return { ok: false };
27
35
  }
@@ -31,9 +39,9 @@ async function probeGitDirty(cwd, runner) {
31
39
  .filter((line) => line.length > 0).length;
32
40
  return { dirty: dirtyFileCount > 0, dirtyFileCount, ok: true };
33
41
  }
34
- export async function probeGitDynamic(cwd = process.cwd(), runner = execFileAsync) {
35
- const branchProbe = await readGitTrimmed(["branch", "--show-current"], cwd, runner);
36
- const dirtyProbe = await probeGitDirty(cwd, runner);
42
+ export async function probeGitDynamic(cwd = process.cwd(), runner = execFileAsync, signal) {
43
+ const branchProbe = await readGitTrimmed(["branch", "--show-current"], cwd, runner, signal);
44
+ const dirtyProbe = await probeGitDirty(cwd, runner, signal);
37
45
  return {
38
46
  ...(branchProbe.value ? { branch: branchProbe.value } : {}),
39
47
  ...(typeof dirtyProbe.dirty === "boolean" ? { dirty: dirtyProbe.dirty } : {}),
@@ -43,9 +51,10 @@ export async function probeGitDynamic(cwd = process.cwd(), runner = execFileAsyn
43
51
  probeFailed: !branchProbe.ok || !dirtyProbe.ok,
44
52
  };
45
53
  }
46
- export async function probeGitContext(cwd = process.cwd(), runner = execFileAsync) {
47
- const repoRoot = (await readGitTrimmed(["rev-parse", "--show-toplevel"], cwd, runner)).value;
48
- const dynamic = await probeGitDynamic(cwd, runner);
54
+ export async function probeGitContext(cwd = process.cwd(), runner = execFileAsync, signal) {
55
+ const repoRoot = (await readGitTrimmed(["rev-parse", "--show-toplevel"], cwd, runner, signal))
56
+ .value;
57
+ const dynamic = await probeGitDynamic(cwd, runner, signal);
49
58
  const resolvedRepoRoot = repoRoot ?? cwd;
50
59
  return {
51
60
  cwd,
@@ -58,31 +67,3 @@ export async function probeGitContext(cwd = process.cwd(), runner = execFileAsyn
58
67
  : {}),
59
68
  };
60
69
  }
61
- export function createGitContextCache(loader) {
62
- let cached = null;
63
- let inflight = null;
64
- return {
65
- async get() {
66
- if (cached)
67
- return cached;
68
- if (inflight)
69
- return inflight;
70
- inflight = loader()
71
- .then((result) => {
72
- cached = result;
73
- return result;
74
- })
75
- .finally(() => {
76
- inflight = null;
77
- });
78
- return inflight;
79
- },
80
- peek() {
81
- return cached;
82
- },
83
- clear() {
84
- cached = null;
85
- inflight = null;
86
- },
87
- };
88
- }
@@ -14,6 +14,7 @@ export const READ_ONLY_TOOLS = new Set([
14
14
  "slack:canvas_comments_read",
15
15
  "slack:confirm_action",
16
16
  "pinet:agents",
17
+ "pinet:lanes",
17
18
  "pinet:read",
18
19
  "memory_read",
19
20
  "memory_search",
@@ -47,10 +48,14 @@ export const WRITE_TOOLS = new Set([
47
48
  "pinet:schedule",
48
49
  "pinet:send",
49
50
  "pinet:snooze",
51
+ "pinet:spawn",
52
+ "pinet:lanes:write",
50
53
  "pinet:free",
51
54
  "pinet:ports",
52
55
  "pinet:reload",
53
56
  "pinet:exit",
57
+ "pinet:hibernate",
58
+ "pinet:wake",
54
59
  ]);
55
60
  /**
56
61
  * Match a tool name against glob patterns (supports `*` wildcard).
package/dist/helpers.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type RuntimeScopeCarrier } from "@pinet/transport-core";
2
+ import { createAbortError, isAbortError } from "@pinet/transport-core/async";
2
3
  import type { ReactionCommandSettings } from "./reaction-triggers.js";
3
4
  import type { AgentSessionSummary } from "./broker/types.js";
4
5
  export interface SlackIngressGuardSettings {
@@ -29,6 +30,8 @@ export interface SlackBridgeSettings {
29
30
  }[];
30
31
  reactionCommands?: ReactionCommandSettings;
31
32
  runtimeMode?: "off" | "single" | "broker" | "follower";
33
+ /** Runtime backend for newly spawned subtree workers. Defaults to tmux. */
34
+ subtreeWorkerRuntime?: "tmux" | "herdr";
32
35
  brokerPrompt?: string;
33
36
  autoConnect?: boolean;
34
37
  autoFollow?: boolean;
@@ -42,6 +45,18 @@ export interface SlackBridgeSettings {
42
45
  agentEmoji?: string;
43
46
  meshSecret?: string;
44
47
  meshSecretPath?: string;
48
+ hibernation?: {
49
+ /** Master kill switch. Defaults false; waking/draining existing rows remains permitted. */
50
+ enabled?: boolean;
51
+ mode?: "observe" | "manual" | "auto";
52
+ allowedRepos?: string[];
53
+ graceMs?: number;
54
+ idleDebounceMs?: number;
55
+ handshakeTimeoutMs?: number;
56
+ wakeLeaseMs?: number;
57
+ maxConcurrentWakes?: number;
58
+ maxConcurrentWakesPerRepo?: number;
59
+ };
45
60
  imessage?: {
46
61
  enabled?: boolean;
47
62
  };
@@ -71,6 +86,7 @@ export declare function describeSlackUserAccess(allowlist: Set<string> | null, o
71
86
  }): string;
72
87
  export declare function getSlackUserAccessWarning(allowlist: Set<string> | null): string | null;
73
88
  export declare function isUserAllowed(allowlist: Set<string> | null, userId: string): boolean;
89
+ export type InboxMessageMetadata = Record<string, unknown>;
74
90
  export interface InboxMessage {
75
91
  channel: string;
76
92
  threadTs: string;
@@ -79,7 +95,8 @@ export interface InboxMessage {
79
95
  timestamp: string;
80
96
  isChannelMention?: boolean;
81
97
  brokerInboxId?: number;
82
- metadata?: Record<string, unknown> | null;
98
+ brokerInboxOrigin?: "subtree";
99
+ metadata?: InboxMessageMetadata | null;
83
100
  scope?: RuntimeScopeCarrier | null;
84
101
  }
85
102
  export interface SqliteJournalModeResult {
@@ -120,36 +137,62 @@ export interface PinetRuntimeReloader<State> {
120
137
  startRuntime: (role: "broker" | "follower") => Promise<void>;
121
138
  }
122
139
  export declare function reloadPinetRuntimeSafely<State>(reloader: PinetRuntimeReloader<State>): Promise<void>;
123
- export interface PinetControlEnvelope {
140
+ export declare function reloadPinetRuntimeInPlaceSafely<State>(input: {
141
+ snapshotState: () => State;
142
+ restoreState: (snapshot: State) => void;
143
+ refreshState: () => void;
144
+ validateRefreshedState: () => void | Promise<void>;
145
+ reloadRuntime: () => Promise<void>;
146
+ }): Promise<void>;
147
+ export type PinetControlMetadata = Record<string, unknown>;
148
+ export interface PinetControlEnvelope extends PinetControlMetadata {
124
149
  type: "pinet:control";
125
150
  action: PinetControlCommand;
126
151
  }
127
152
  export declare function getPinetControlCommandFromText(text: string | undefined): PinetControlCommand | null;
128
153
  export declare function buildPinetControlMetadata(command: PinetControlCommand): PinetControlEnvelope;
129
154
  export declare function buildPinetControlMessage(command: PinetControlCommand): string;
130
- export declare function normalizeOutgoingPinetControlMessage(body: string, metadata?: Record<string, unknown>): {
155
+ export declare function normalizeOutgoingPinetControlMessage(body: string, metadata?: PinetControlMetadata): {
156
+ body: string;
157
+ metadata: PinetControlMetadata;
158
+ } | null;
159
+ export interface PinetSteeringEnvelope extends PinetControlMetadata {
160
+ type: "pinet:steer";
161
+ message: string;
162
+ }
163
+ export declare function getPinetSteeringMessageFromText(text: string | undefined): string | null;
164
+ export declare function buildPinetSteeringMetadata(message: string): PinetSteeringEnvelope;
165
+ export declare function buildPinetSteeringMessage(message: string): string;
166
+ export declare function normalizeOutgoingPinetSteeringMessage(body: string, metadata?: PinetControlMetadata): {
131
167
  body: string;
132
- metadata: Record<string, unknown>;
168
+ metadata: PinetControlMetadata;
133
169
  } | null;
134
170
  export type PinetSkinStatusKey = "idle" | "working" | "healthy" | "stale" | "ghost" | "resumable";
135
171
  export type PinetSkinStatusVocabulary = Partial<Record<PinetSkinStatusKey, string>>;
136
- export declare function extractPinetControlCommand(message: {
172
+ export interface PinetControlCommandMessage {
173
+ threadId?: string;
174
+ body?: string;
175
+ metadata?: PinetControlMetadata | null;
176
+ }
177
+ export declare function extractPinetControlCommand(message: PinetControlCommandMessage): PinetControlCommand | null;
178
+ export declare function extractPinetSteeringMessage(message: {
137
179
  threadId?: string;
138
180
  body?: string;
139
- metadata?: Record<string, unknown> | null;
140
- }): PinetControlCommand | null;
181
+ metadata?: PinetControlMetadata | null;
182
+ }): string | null;
141
183
  export declare const FORM_METHODS: Set<string>;
142
- export declare function buildSlackRequest(method: string, token: string, body?: Record<string, unknown>): {
184
+ export type SlackRequestBody = Record<string, unknown>;
185
+ export interface SlackApiRequest {
143
186
  url: string;
144
187
  init: RequestInit;
145
- };
188
+ }
189
+ export declare function buildSlackRequest(method: string, token: string, body?: SlackRequestBody): SlackApiRequest;
146
190
  export interface AbortableOperationTracker {
147
191
  run<T>(operation: (signal: AbortSignal) => Promise<T>): Promise<T>;
148
192
  abortAndWait(): Promise<void>;
149
193
  isAborting(): boolean;
150
194
  }
151
- export declare function createAbortError(message?: string): Error;
152
- export declare function isAbortError(error: unknown): boolean;
195
+ export { createAbortError, isAbortError };
153
196
  export declare function abortableDelay(ms: number, signal: AbortSignal): Promise<void>;
154
197
  export declare function createAbortableOperationTracker(): AbortableOperationTracker;
155
198
  export declare function stripBotMention(text: string, botUserId: string): string;
@@ -220,6 +263,7 @@ export interface AgentDisplayInfo {
220
263
  routingScore?: number;
221
264
  routingReasons?: string[];
222
265
  }
266
+ export type AgentVisibilityMetadata = Record<string, unknown>;
223
267
  export interface AgentVisibilityInput {
224
268
  emoji: string;
225
269
  name: string;
@@ -228,7 +272,7 @@ export interface AgentVisibilityInput {
228
272
  stableId?: string | null;
229
273
  session?: AgentSessionSummary | null;
230
274
  status: "working" | "idle";
231
- metadata?: Record<string, unknown> | null;
275
+ metadata?: AgentVisibilityMetadata | null;
232
276
  lastHeartbeat?: string;
233
277
  lastSeen?: string;
234
278
  disconnectedAt?: string | null;
@@ -257,7 +301,7 @@ export interface AgentRoutingHint {
257
301
  task?: string;
258
302
  }
259
303
  export declare function shortenPath(p: string, homedir: string): string;
260
- export declare function extractAgentCapabilities(metadata: Record<string, unknown> | null | undefined): AgentCapabilities;
304
+ export declare function extractAgentCapabilities(metadata: AgentVisibilityMetadata | null | undefined): AgentCapabilities;
261
305
  export declare function buildAgentCapabilityTags(capabilities: AgentCapabilities): string[];
262
306
  export interface MeshVisibilityOptions {
263
307
  includeGhosts?: boolean;
@@ -379,6 +423,7 @@ export interface FollowerThreadState {
379
423
  source?: string;
380
424
  owner?: string;
381
425
  }
426
+ export type FollowerInboxMessageMetadata = Record<string, unknown>;
382
427
  export interface FollowerInboxEntry {
383
428
  inboxId?: number;
384
429
  message: {
@@ -387,7 +432,7 @@ export interface FollowerInboxEntry {
387
432
  sender?: string;
388
433
  body?: string;
389
434
  createdAt?: string;
390
- metadata: Record<string, unknown> | null;
435
+ metadata: FollowerInboxMessageMetadata | null;
391
436
  };
392
437
  }
393
438
  export interface FollowerInboxSyncResult {
@@ -404,13 +449,21 @@ export interface BrokerInboxControlEntry {
404
449
  inboxId: number;
405
450
  command: PinetControlCommand;
406
451
  }
452
+ export interface PinetSteeringInboxEntry {
453
+ inboxId: number;
454
+ message: FollowerInboxEntry["message"];
455
+ steeringText: string;
456
+ }
407
457
  export interface BrokerInboxSyncResult {
408
458
  controlEntries: BrokerInboxControlEntry[];
459
+ steeringEntries: PinetSteeringInboxEntry[];
409
460
  inboxMessages: InboxMessage[];
410
461
  }
411
462
  export declare function isDirectMessageChannel(channel: string): boolean;
412
463
  export declare function syncTransferredSlackThreadContexts(entries: FollowerInboxEntry[], existingThreads: ReadonlyMap<string, FollowerThreadState>, agentOwner: string): TransferredSlackThreadSyncResult;
413
464
  export declare function syncFollowerInboxEntries(entries: FollowerInboxEntry[], existingThreads: ReadonlyMap<string, FollowerThreadState>, agentOwner: string, lastDmChannel: string | null): FollowerInboxSyncResult;
465
+ export declare function extractPinetSteeringInboxEntry(entry: FollowerInboxEntry): PinetSteeringInboxEntry | null;
466
+ export declare function formatPinetSteeringMessage(entry: PinetSteeringInboxEntry): string;
414
467
  export declare function syncBrokerInboxEntries(entries: FollowerInboxEntry[]): BrokerInboxSyncResult;
415
468
  export interface FollowerThreadChannelResolution {
416
469
  channelId: string | null;
@@ -542,24 +595,21 @@ export interface CallSlackAPIOptions {
542
595
  signal?: AbortSignal;
543
596
  retryCount?: number;
544
597
  }
545
- export declare function callSlackAPI(method: string, token: string, body?: Record<string, unknown>, options?: CallSlackAPIOptions): Promise<SlackResult>;
546
- export declare function isRalphNudgeEntry(entry: {
598
+ export declare function callSlackAPI(method: string, token: string, body?: SlackRequestBody, options?: CallSlackAPIOptions): Promise<SlackResult>;
599
+ interface FollowerInboxMetadataEntry {
547
600
  message: {
548
- metadata?: Record<string, unknown> | null;
601
+ metadata?: FollowerInboxMessageMetadata | null;
549
602
  };
550
- }): boolean;
551
- export declare function isAgentToAgentEntry(entry: {
552
- message: {
553
- threadId?: string;
554
- metadata?: Record<string, unknown> | null;
555
- };
556
- }): boolean;
557
- export declare function partitionFollowerInboxEntries<T extends {
603
+ }
604
+ interface FollowerInboxPartitionEntry extends FollowerInboxMetadataEntry {
558
605
  message: {
559
606
  threadId?: string;
560
- metadata?: Record<string, unknown> | null;
607
+ metadata?: FollowerInboxMessageMetadata | null;
561
608
  };
562
- }>(entries: T[]): {
609
+ }
610
+ export declare function isRalphNudgeEntry(entry: FollowerInboxMetadataEntry): boolean;
611
+ export declare function isAgentToAgentEntry(entry: FollowerInboxPartitionEntry): boolean;
612
+ export declare function partitionFollowerInboxEntries<T extends FollowerInboxPartitionEntry>(entries: T[]): {
563
613
  nudges: T[];
564
614
  agentMessages: T[];
565
615
  regular: T[];