@pinet/slack-bridge 0.2.4 → 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.
- package/README.md +26 -1
- package/dist/activity-log.d.ts +10 -3
- package/dist/agent-event-runtime.d.ts +0 -2
- package/dist/agent-event-runtime.js +0 -1
- package/dist/agent-prompt-guidance.d.ts +1 -6
- package/dist/agent-prompt-guidance.js +1 -6
- package/dist/broker/adapters/slack.d.ts +4 -1
- package/dist/broker/adapters/slack.js +16 -9
- package/dist/broker/client.d.ts +21 -4
- package/dist/broker/client.js +65 -25
- package/dist/broker/hibernation-activation-authority.d.ts +22 -0
- package/dist/broker/hibernation-activation-authority.js +51 -0
- package/dist/broker/hibernation-activation.d.ts +75 -0
- package/dist/broker/hibernation-activation.js +95 -0
- package/dist/broker/hibernation-command-router.d.ts +54 -0
- package/dist/broker/hibernation-command-router.js +124 -0
- package/dist/broker/hibernation-runtime-adapters.d.ts +79 -0
- package/dist/broker/hibernation-runtime-adapters.js +473 -0
- package/dist/broker/hibernation-runtime-helpers.d.ts +146 -0
- package/dist/broker/hibernation-runtime-helpers.js +243 -0
- package/dist/broker/index.d.ts +16 -1
- package/dist/broker/index.js +68 -10
- package/dist/broker/lock-conflict.d.ts +109 -0
- package/dist/broker/lock-conflict.js +409 -0
- package/dist/broker/socket-server.d.ts +25 -0
- package/dist/broker/socket-server.js +234 -4
- package/dist/broker-delivery.d.ts +1 -0
- package/dist/broker-delivery.js +10 -1
- package/dist/broker-runtime.d.ts +14 -2
- package/dist/broker-runtime.js +114 -3
- package/dist/canvases.d.ts +12 -2
- package/dist/canvases.js +1 -3
- package/dist/compaction-gate.d.ts +7 -0
- package/dist/compaction-gate.js +35 -0
- package/dist/deploy-manifest.d.ts +8 -2
- package/dist/deploy-manifest.js +9 -7
- package/dist/follower-runtime.d.ts +5 -2
- package/dist/follower-runtime.js +39 -5
- package/dist/git-metadata.d.ts +5 -9
- package/dist/git-metadata.js +23 -42
- package/dist/guardrails.js +5 -0
- package/dist/helpers.d.ts +77 -27
- package/dist/helpers.js +150 -39
- package/dist/hibernation-config.d.ts +13 -0
- package/dist/hibernation-config.js +14 -0
- package/dist/home-tab.d.ts +13 -2
- package/dist/imessage-tools.d.ts +4 -1
- package/dist/inbox-drain-runtime.d.ts +1 -0
- package/dist/inbox-drain-runtime.js +5 -1
- package/dist/index.js +340 -108
- package/dist/pinet-commands.d.ts +14 -1
- package/dist/pinet-commands.js +93 -22
- package/dist/pinet-mesh-ops.d.ts +6 -4
- package/dist/pinet-mesh-ops.js +8 -1
- package/dist/pinet-runtime-composition.d.ts +2 -1
- package/dist/pinet-runtime-composition.js +14 -3
- package/dist/pinet-session-formatting.d.ts +26 -2
- package/dist/pinet-session-formatting.js +2 -0
- package/dist/pinet-tools.d.ts +14 -0
- package/dist/pinet-tools.js +355 -50
- package/dist/prompts/broker/default.md +3 -3
- package/dist/prompts/broker/tmux.md +3 -3
- package/dist/runtime-agent-context.d.ts +3 -3
- package/dist/runtime-agent-context.js +12 -6
- package/dist/runtime-mode.js +4 -0
- package/dist/session-ui-runtime.js +3 -1
- package/dist/single-player-runtime.d.ts +1 -1
- package/dist/single-player-runtime.js +21 -5
- package/dist/slack-access.d.ts +10 -2
- package/dist/slack-access.js +64 -22
- package/dist/slack-block-kit.d.ts +44 -2
- package/dist/slack-block-kit.js +5 -5
- package/dist/slack-file-access.d.ts +12 -1
- package/dist/slack-file-access.js +4 -4
- package/dist/slack-message-context.d.ts +9 -2
- package/dist/slack-modals.d.ts +4 -1
- package/dist/slack-modals.js +5 -5
- package/dist/slack-pinet-runtime-adapter.d.ts +1 -0
- package/dist/slack-pinet-runtime-adapter.js +10 -0
- package/dist/slack-presence.d.ts +5 -4
- package/dist/slack-request-runtime.d.ts +3 -2
- package/dist/slack-scope-diagnostics.d.ts +27 -2
- package/dist/slack-socket-dedup.d.ts +8 -5
- package/dist/slack-tools.d.ts +3 -1
- package/dist/slack-tools.js +26 -16
- package/dist/slack-upload.d.ts +17 -1
- package/dist/subtree-broker-runtime.d.ts +83 -2
- package/dist/subtree-broker-runtime.js +774 -109
- package/dist/tool-output-limits.d.ts +11 -0
- package/dist/tool-output-limits.js +20 -0
- package/dist/tool-registration-runtime.d.ts +3 -0
- package/dist/tool-registration-runtime.js +27 -0
- package/package.json +8 -8
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Phase B live-runtime activation composition.
|
|
2
|
+
//
|
|
3
|
+
// Wires the reviewed, approved hibernation RUNTIME ADAPTERS (real process/tmux
|
|
4
|
+
// controllers + spawn-authored runtime-spec builder + git-remote VCS identity)
|
|
5
|
+
// into the broker's three live seams, all behind the DEFAULT-OFF durable,
|
|
6
|
+
// non-reloadable activation authority (see hibernation-activation-authority.ts;
|
|
7
|
+
// `hibernationRuntimeActive()`):
|
|
8
|
+
//
|
|
9
|
+
// 1. createHibernationOrchestrator — the real orchestrator-backed executor
|
|
10
|
+
// that replaces the `activation_pending` stub ONLY under the gate.
|
|
11
|
+
// 2. persistSpawnedRuntimeSpec — records a durable, broker-authored
|
|
12
|
+
// runtime spec (canonical git-remote VCS identity) at worker spawn.
|
|
13
|
+
// 3. recoverStrandedWakesBeforeRegistrations — reconciles crash-stranded wake
|
|
14
|
+
// rows at broker startup, before the socket accepts new registrations.
|
|
15
|
+
//
|
|
16
|
+
// Keeping the composition here (rather than inline in index.ts / the subtree
|
|
17
|
+
// runtime) makes every seam unit- and E2E-testable against REAL dependencies
|
|
18
|
+
// (real BrokerDB, real git, real tmux/pi) with no production side effects.
|
|
19
|
+
import { HibernationOrchestrator, } from "@pinet/broker-core";
|
|
20
|
+
import { createHibernationProcessController, createHibernationTmuxController, resolveVcsIdentity, } from "./hibernation-runtime-adapters.js";
|
|
21
|
+
import { buildRuntimeSpecInput, } from "./hibernation-runtime-helpers.js";
|
|
22
|
+
import { hibernationActivationAuthorized } from "./hibernation-activation-authority.js";
|
|
23
|
+
/**
|
|
24
|
+
* The DEFAULT-OFF live-runtime gate. Live process/tmux composition happens ONLY
|
|
25
|
+
* when the durable, process-lifetime activation authority — captured at broker
|
|
26
|
+
* start from the external launch environment, never agent-editable settings — is
|
|
27
|
+
* set. Config reloads and settings edits can NEVER flip it, so the production
|
|
28
|
+
* default (authority unset) keeps the broker on the `activation_pending` stub
|
|
29
|
+
* with zero live-runtime side effects. Operational permission (enabled / mode /
|
|
30
|
+
* repo allowlist) is a SEPARATE settings policy enforced by the command layer;
|
|
31
|
+
* this gate only decides whether the real machinery is wired in at all.
|
|
32
|
+
*/
|
|
33
|
+
export function hibernationRuntimeActive() {
|
|
34
|
+
return hibernationActivationAuthorized();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Seam 1 — compose the real orchestrator-backed executor.
|
|
38
|
+
*
|
|
39
|
+
* The process and tmux controllers are constructed INDEPENDENTLY: the immutable
|
|
40
|
+
* launch generation rides inside the attempt handle `respawnRuntime` returns, so
|
|
41
|
+
* attempt-scoped stop/liveness bind to the exact launched process with no shared
|
|
42
|
+
* registry. The returned {@link HibernationOrchestrator} structurally satisfies
|
|
43
|
+
* `HibernateCommandExecutor & WakeCommandExecutor`, so it drops directly into the
|
|
44
|
+
* command executor slot in place of the `activation_pending` stub — but only the
|
|
45
|
+
* caller's {@link hibernationRuntimeActive} gate decides when to use it.
|
|
46
|
+
*/
|
|
47
|
+
export function createHibernationOrchestrator(deps) {
|
|
48
|
+
const process = createHibernationProcessController({
|
|
49
|
+
pendingInboxCount: (agentId) => deps.db.getUnreadInboxCount(agentId),
|
|
50
|
+
});
|
|
51
|
+
const tmux = createHibernationTmuxController({
|
|
52
|
+
extensionEntryPath: deps.extensionEntryPath,
|
|
53
|
+
baseLaunchEnv: deps.baseLaunchEnv,
|
|
54
|
+
inheritedEnvKeys: deps.inheritedEnvKeys,
|
|
55
|
+
});
|
|
56
|
+
return new HibernationOrchestrator({
|
|
57
|
+
db: deps.db,
|
|
58
|
+
process,
|
|
59
|
+
tmux,
|
|
60
|
+
brokerInstanceId: deps.brokerInstanceId,
|
|
61
|
+
...(deps.config ? { config: deps.config } : {}),
|
|
62
|
+
...(deps.awaitRuntimeRegistration
|
|
63
|
+
? { awaitRuntimeRegistration: deps.awaitRuntimeRegistration }
|
|
64
|
+
: {}),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Seam 2 — persist a durable, broker-authored runtime spec at worker spawn.
|
|
69
|
+
*
|
|
70
|
+
* The canonical `owner/repo` VCS identity (the ONLY value the repo allowlist
|
|
71
|
+
* authorizes against) is resolved HERE from the runtime's actual git `origin`
|
|
72
|
+
* remote — never from filesystem directory names. Fails closed: an unresolvable
|
|
73
|
+
* remote records `vcsIdentity: null` (the fail-closed gate then refuses), and a
|
|
74
|
+
* spec that could not be safely hibernated/woken (missing session/tmux/repo
|
|
75
|
+
* locators) is never written. Returns the persisted spec, or null when the facts
|
|
76
|
+
* do not compose a durable spec.
|
|
77
|
+
*/
|
|
78
|
+
export async function persistSpawnedRuntimeSpec(db, facts, runner) {
|
|
79
|
+
const vcsIdentity = await resolveVcsIdentity(facts.repoRoot, runner);
|
|
80
|
+
const input = buildRuntimeSpecInput({ ...facts, vcsIdentity });
|
|
81
|
+
if (!input)
|
|
82
|
+
return null;
|
|
83
|
+
return db.upsertAgentRuntimeSpec(input);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Seam 3 — reconcile crash-stranded wake state at broker startup.
|
|
87
|
+
*
|
|
88
|
+
* Must run BEFORE the broker socket accepts new registrations so a stranded
|
|
89
|
+
* `waking`/`dispatching` row is completed, quarantined, or requeued deterministically
|
|
90
|
+
* rather than racing an incoming (possibly duplicate) wake registration. This is a
|
|
91
|
+
* synchronous DB reconciliation; it launches nothing.
|
|
92
|
+
*/
|
|
93
|
+
export function recoverStrandedWakesBeforeRegistrations(orchestrator) {
|
|
94
|
+
return orchestrator.recoverStrandedWakes();
|
|
95
|
+
}
|
|
@@ -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 {};
|