@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
@@ -0,0 +1,54 @@
1
+ import { type HibernationCommandResult, type RuntimeLaunchContext } from "@pinet/broker-core";
2
+ import type { BrokerDB } from "./schema.js";
3
+ import type { ResolvedHibernationSettings } from "../hibernation-config.js";
4
+ /**
5
+ * The authoritative runtime-control surface the command path binds to when runtime
6
+ * activation is active. Structurally identical to the subtree broker's
7
+ * `SubtreeHibernationRuntimeControl`; kept here so the router has no import
8
+ * dependency on the extension wiring and stays independently testable.
9
+ */
10
+ export interface HibernationRuntimeControl {
11
+ /** The ONE authoritative DB that owns the spawned workers + their specs. */
12
+ db: BrokerDB;
13
+ /** Broker instance id recorded on lifecycle leases; matches startup recovery. */
14
+ brokerInstanceId: string;
15
+ /** Base PINET_* env re-establishing the mesh connection for a woken worker. */
16
+ baseLaunchEnv: Record<string, string>;
17
+ }
18
+ export interface RouteHibernationCommandInput {
19
+ command: "hibernate" | "wake";
20
+ target: string;
21
+ reason?: string;
22
+ /** Must be "broker"; any other role (or null) rejects (this session is not the broker). */
23
+ brokerRole: string | null;
24
+ /** Operational policy (enabled/mode/allowlist) — SEPARATE from runtime activation. */
25
+ hib: ResolvedHibernationSettings;
26
+ /**
27
+ * Real accessor for the subtree broker's authoritative control surface. Invoked
28
+ * only when the frozen activation authority is set; returns null when no subtree
29
+ * broker is running (nothing is command-addressable).
30
+ */
31
+ getRuntimeControl: () => HibernationRuntimeControl | null;
32
+ /** Central broker DB, used ONLY as the default-off fallback (never when active). */
33
+ getFallbackDb: () => BrokerDB | null;
34
+ /** slack-bridge extension entry a woken runtime loads (`pi -e <path>`). */
35
+ extensionEntryPath: string;
36
+ /** Broker env var NAMES (never values) re-exported into a woken runtime. */
37
+ inheritedEnvKeys: string[];
38
+ /**
39
+ * Socket-registration RPC stand-in. PRODUCTION leaves this undefined so the
40
+ * orchestrator's default polls the broker DB until the socket server's fenced
41
+ * handler accepts the woken worker's generation. Injectable so an isolated E2E
42
+ * can key acceptance off the REAL respawned process instead of a live handshake.
43
+ */
44
+ awaitRuntimeRegistration?: (ctx: RuntimeLaunchContext) => Promise<boolean>;
45
+ }
46
+ /**
47
+ * Resolve and run one hibernate/wake command exactly as the live extension does.
48
+ *
49
+ * Ordering matters and is asserted by regression tests: activation is read from
50
+ * the frozen authority FIRST, which decides whether the authoritative DB is the
51
+ * subtree control DB or the central fallback, which decides whether the real
52
+ * orchestrator or the `activation_pending` stub is composed.
53
+ */
54
+ export declare function routeHibernationCommand(input: RouteHibernationCommandInput): Promise<HibernationCommandResult>;
@@ -0,0 +1,124 @@
1
+ // Production hibernate/wake command router + executor composition.
2
+ //
3
+ // This is the SINGLE source of truth for how an operator `pinet hibernate` /
4
+ // `pinet wake` command resolves its authoritative DB, target worker, policy, and
5
+ // executor. It is extracted out of the extension closure so BOTH the live
6
+ // extension (`index.ts` `runHibernationCommand`) and the isolated production-route
7
+ // E2E drive the exact same integration code — the code that fixed the DB-topology
8
+ // and activation-authority seams — rather than re-composing the pieces by hand.
9
+ //
10
+ // The design invariants this router encodes (each has a negative regression test):
11
+ //
12
+ // • Activation is authorized ONLY by the durable, non-reloadable, frozen
13
+ // broker-start authority (`hibernationRuntimeActive()`), never by reloadable
14
+ // settings. A settings reload can NEVER flip a running broker on.
15
+ // • When active, the SUBTREE broker is the authority: it spawned and OWNS these
16
+ // workers and authored their durable runtime specs, so target resolution, the
17
+ // authz spec read, AND the orchestrator all run against that SAME subtree DB
18
+ // (`getRuntimeControl().db`) — never the central broker DB.
19
+ // • When inactive (production default) there is no runtime control, so resolution
20
+ // falls back to the central DB and the executor stays the `activation_pending`
21
+ // stub: a clear refusal, never a silent no-op.
22
+ // • The repo allowlist is a security boundary authorized ONLY against the
23
+ // broker-authored spec's canonical `owner/repo` VCS identity (captured at spawn
24
+ // from the git remote), never a filesystem path slug or worker-declared metadata.
25
+ import { executeHibernateCommand, executeWakeCommand, unknownHibernationTarget, } from "@pinet/broker-core";
26
+ import { createHibernationOrchestrator, hibernationRuntimeActive, } from "./hibernation-activation.js";
27
+ /**
28
+ * Resolve and run one hibernate/wake command exactly as the live extension does.
29
+ *
30
+ * Ordering matters and is asserted by regression tests: activation is read from
31
+ * the frozen authority FIRST, which decides whether the authoritative DB is the
32
+ * subtree control DB or the central fallback, which decides whether the real
33
+ * orchestrator or the `activation_pending` stub is composed.
34
+ */
35
+ export async function routeHibernationCommand(input) {
36
+ const { command, target, reason, brokerRole, hib } = input;
37
+ if (brokerRole !== "broker") {
38
+ throw new Error(`pinet ${command} is broker-managed. Connect this session as the broker (/pinet start) to hibernate or wake workers.`);
39
+ }
40
+ // Authoritative DB unification + explicit trust boundary. When the durable,
41
+ // non-reloadable runtime-activation authority is set, the SUBTREE broker owns
42
+ // these workers AND authored their durable runtime specs, so hibernate/wake must
43
+ // resolve the target, read its authz spec, AND drive the orchestrator against
44
+ // that SAME subtree DB end to end. The operator may therefore only address
45
+ // workers this process's subtree broker owns. In the default-off configuration
46
+ // there is no runtime control, so resolution falls back to the central broker DB
47
+ // and the executor stays the `activation_pending` stub (a clear refusal, never a
48
+ // silent no-op).
49
+ const runtimeControl = hibernationRuntimeActive() ? input.getRuntimeControl() : null;
50
+ const db = runtimeControl?.db ?? input.getFallbackDb();
51
+ if (!db)
52
+ throw new Error("Broker database is unavailable.");
53
+ const wanted = target.trim().replace(/^@/, "");
54
+ const agents = db.getAllAgents();
55
+ const lowerWanted = wanted.toLowerCase();
56
+ const nameMatches = agents.filter((a) => a.name?.toLowerCase() === lowerWanted);
57
+ const agent = agents.find((a) => a.id === wanted) ??
58
+ agents.find((a) => a.stableId === wanted) ??
59
+ (nameMatches.length === 1 ? nameMatches[0] : undefined);
60
+ if (!agent)
61
+ return unknownHibernationTarget(command, target);
62
+ const policy = {
63
+ enabled: hib.enabled,
64
+ mode: hib.mode,
65
+ allowedRepos: hib.allowedRepos,
66
+ };
67
+ const state = (agent.lifecycleState ?? "live");
68
+ // Provenance: the repo allowlist is a security boundary, so authorization trusts
69
+ // ONLY the broker-authored durable runtime spec's CANONICAL VCS IDENTITY
70
+ // (`owner/repo`), captured at spawn from the runtime's git remote. Ownership is
71
+ // NEVER inferred from filesystem directory names or worker-declared metadata.
72
+ // When no trusted spec / resolvable remote exists the identifier stays null and
73
+ // the fail-closed gate refuses.
74
+ const repoIdentifier = db.getAgentRuntimeSpec(agent.id)?.vcsIdentity ?? null;
75
+ // Live process/tmux checkpoint/respawn adapters are a separate, explicitly gated
76
+ // activation step (default-off). When runtime activation is unset `runtimeControl`
77
+ // is null and callers get a clear `activation_pending` refusal rather than a
78
+ // silent no-op. When set, the real process/tmux HibernationOrchestrator is
79
+ // composed over the SAME subtree DB that owns the worker and its spec, with
80
+ // `brokerInstanceId` matching the spawn/startup-recovery owner so lease fencing
81
+ // lines up. The woken worker's respawn env is the subtree broker's child-launch
82
+ // env; inherited secret var NAMES come from the shared spawn/wake allowlist.
83
+ const executor = runtimeControl
84
+ ? createHibernationOrchestrator({
85
+ db: runtimeControl.db,
86
+ brokerInstanceId: runtimeControl.brokerInstanceId,
87
+ extensionEntryPath: input.extensionEntryPath,
88
+ baseLaunchEnv: runtimeControl.baseLaunchEnv,
89
+ inheritedEnvKeys: input.inheritedEnvKeys,
90
+ config: {
91
+ handshakeTimeoutMs: hib.handshakeTimeoutMs,
92
+ wakeLeaseMs: hib.wakeLeaseMs,
93
+ maxConcurrentWakes: hib.maxConcurrentWakes,
94
+ maxConcurrentWakesPerRepo: hib.maxConcurrentWakesPerRepo,
95
+ },
96
+ ...(input.awaitRuntimeRegistration
97
+ ? { awaitRuntimeRegistration: input.awaitRuntimeRegistration }
98
+ : {}),
99
+ })
100
+ : {
101
+ prepareHibernation: () => ({ ready: false, state, reason: "activation_pending" }),
102
+ hibernate: async () => ({ ok: false, state, reason: "activation_pending" }),
103
+ wake: async () => ({ ok: false, state, reason: "activation_pending" }),
104
+ };
105
+ if (command === "hibernate") {
106
+ return executeHibernateCommand({
107
+ executor,
108
+ agentId: agent.id,
109
+ state,
110
+ repoIdentifier,
111
+ policy,
112
+ actor: "operator",
113
+ reason,
114
+ });
115
+ }
116
+ return executeWakeCommand({
117
+ executor,
118
+ agentId: agent.id,
119
+ state,
120
+ policy,
121
+ actor: "operator",
122
+ reason,
123
+ });
124
+ }
@@ -0,0 +1,79 @@
1
+ import type { HibernationProcessController, HibernationTmuxController, RuntimeLaunchContext } from "@pinet/broker-core";
2
+ export interface CommandResult {
3
+ stdout: string;
4
+ stderr: string;
5
+ code: number;
6
+ }
7
+ /** Minimal command executor abstraction (fake in tests, execFile at runtime). */
8
+ export interface CommandRunner {
9
+ run(file: string, args: string[]): Promise<CommandResult>;
10
+ }
11
+ /** Real `CommandRunner` over `child_process.execFile` that never throws on a
12
+ * non-zero exit — it resolves the captured exit code so callers branch on it. */
13
+ export declare function createExecFileRunner(): CommandRunner;
14
+ /** The minimal tmux pane address the pane probes/signals operate against. */
15
+ interface PaneAddress {
16
+ tmuxSocket: string;
17
+ tmuxTarget: string;
18
+ }
19
+ /**
20
+ * A verified OS process generation: the pane's foreground pid PLUS an OS
21
+ * generation token (the process START TIME). The token pins the exact process
22
+ * instance so a cross-second reused pid (same number, different process, later
23
+ * start) is not mistaken for — or signalled as — the original runtime. Start time
24
+ * survives the launcher's `exec` of Pi (unlike the command line), so it stays
25
+ * stable for the life of the runtime. See the header note on the residual
26
+ * same-second window.
27
+ */
28
+ interface ProcessGeneration {
29
+ pid: number;
30
+ generationToken: string;
31
+ }
32
+ /**
33
+ * The attempt-bound generation a single wake launched, carrying the pane address
34
+ * so the attempt-scoped stop/liveness probes read the exact pane the attempt
35
+ * respawned into. Embedded in {@link LaunchedAttemptHandle}.
36
+ */
37
+ export interface AttemptGeneration extends ProcessGeneration, PaneAddress {
38
+ }
39
+ export interface RuntimeAdapterDeps {
40
+ runner?: CommandRunner;
41
+ /** Byte size of a session file, or null when absent (non-empty ⇒ resumable). */
42
+ sessionByteSize?: (filePath: string) => number | null;
43
+ processAlive?: (pid: number) => boolean;
44
+ sendSignal?: (pid: number, signal: NodeJS.Signals) => void;
45
+ now?: () => number;
46
+ sleep?: (ms: number) => Promise<void>;
47
+ /** Pending inbox count projection for the checkpoint outcome. */
48
+ pendingInboxCount?: (agentId: string) => number;
49
+ /** Grace window for TERM before escalating to KILL. */
50
+ stopGraceMs?: number;
51
+ /** Poll interval while awaiting process exit. */
52
+ pollMs?: number;
53
+ }
54
+ export declare function createHibernationProcessController(deps?: RuntimeAdapterDeps): HibernationProcessController;
55
+ export interface RuntimeRespawnDeps {
56
+ runner?: CommandRunner;
57
+ /** slack-bridge extension entry the woken runtime loads (`pi -e <path>`). */
58
+ extensionEntryPath: string;
59
+ /** Base PINET_* env re-establishing the mesh connection for the woken worker. */
60
+ baseLaunchEnv: Record<string, string>;
61
+ /** Broker env keys re-exported into the woken runtime when present. */
62
+ inheritedEnvKeys: string[];
63
+ buildNickname?: (ctx: RuntimeLaunchContext) => string;
64
+ /** Private, owner-only directory launchers are materialized in (lazily made). */
65
+ launcherDir?: string;
66
+ writeFile?: (filePath: string, content: string, mode: number) => void;
67
+ unlink?: (filePath: string) => void;
68
+ readEnv?: (key: string) => string | undefined;
69
+ }
70
+ export declare function createHibernationTmuxController(deps: RuntimeRespawnDeps): HibernationTmuxController;
71
+ /**
72
+ * Resolve the canonical git-remote-derived `owner/repo` VCS identity for a repo
73
+ * root at spawn time. This is the ONLY identity the repo allowlist authorizes
74
+ * against — derived from the runtime's actual `origin` remote, never from the
75
+ * filesystem directory name. Returns null when no remote is resolvable (the
76
+ * fail-closed authorization gate then refuses).
77
+ */
78
+ export declare function resolveVcsIdentity(repoRoot: string, runner?: CommandRunner): Promise<string | null>;
79
+ export {};