cyberlegion 0.3.2 → 0.4.0
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/.claude-plugin/plugin.json +20 -0
- package/.codex-plugin/plugin.json +20 -0
- package/.plugin/pins.json +3 -0
- package/agents/headless-legate.md +59 -0
- package/dist/cli.mjs +7699 -69
- package/dist/index.d.mts +243 -7
- package/dist/index.mjs +2214 -4
- package/package.json +8 -2
- package/plugin.json +24 -0
- package/readme.md +60 -0
- package/skills/dispatch-governance/README.md +29 -0
- package/skills/dispatch-governance/SKILL.md +138 -0
- package/skills/init-cyberlegion/README.md +14 -0
- package/skills/init-cyberlegion/SKILL.md +110 -0
- package/skills/legate/README.md +22 -0
- package/skills/legate/SKILL.md +62 -0
- package/skills/manage-inbox/README.md +12 -0
- package/skills/manage-inbox/SKILL.md +77 -0
- package/skills/relay-governance/README.md +36 -0
- package/skills/relay-governance/SKILL.md +199 -0
- package/skills/session-adapter-governance/README.md +17 -0
- package/skills/session-adapter-governance/SKILL.md +74 -0
- package/skills/subagent-backend-governance/README.md +35 -0
- package/skills/subagent-backend-governance/SKILL.md +94 -0
- package/dist/cli.d.mts +0 -12
- package/dist/watch-D33fQcUI.mjs +0 -2044
package/dist/index.d.mts
CHANGED
|
@@ -55,8 +55,15 @@ interface AgentRecord {
|
|
|
55
55
|
brief?: string;
|
|
56
56
|
spawnedBy?: string;
|
|
57
57
|
/** Absent ⇒ session (backward compat, no migration). 'standing' = a session-independent,
|
|
58
|
-
* prune-exempt owner inbox minted by `unit register --standing`.
|
|
59
|
-
|
|
58
|
+
* prune-exempt owner inbox minted by `unit register --standing`. 'service' = a project service's
|
|
59
|
+
* endpoint (`service.ts`): session-independent and prune-exempt like a standing record, but never
|
|
60
|
+
* surfaced as owner mail — its mailbox belongs to whichever unit currently owns the service. */
|
|
61
|
+
kind?: 'session' | 'standing' | 'service';
|
|
62
|
+
/** Only meaningful on a `kind: 'service'` record — which project service this endpoint is. */
|
|
63
|
+
service?: {
|
|
64
|
+
project: string;
|
|
65
|
+
name: string;
|
|
66
|
+
};
|
|
60
67
|
/** Only meaningful on a `kind: 'standing'` record — the id of the unit currently standing in for
|
|
61
68
|
* it (`unit claim`), a per-record singleton pointer that moves as the principal moves between
|
|
62
69
|
* units (last claim wins). Resolved live, never trusted: a presence unit that has exited reads as
|
|
@@ -64,6 +71,42 @@ interface AgentRecord {
|
|
|
64
71
|
* stays inert until re-claimed. Independent of the hub's `mainPane` (the human's read-pane). */
|
|
65
72
|
presence?: string;
|
|
66
73
|
}
|
|
74
|
+
/** A registered project: one git repository, shared by its default checkout and every linked
|
|
75
|
+
* worktree of it. Keyed by its git common dir, never by a pane or a display name. */
|
|
76
|
+
interface ProjectRecord {
|
|
77
|
+
/** Stable reference derived from the canonical git common dir (`project.ts`). */
|
|
78
|
+
id: string;
|
|
79
|
+
/** Display name — the default checkout's directory basename. Not unique; never a key. */
|
|
80
|
+
name: string;
|
|
81
|
+
/** The default checkout's root. */
|
|
82
|
+
root: string;
|
|
83
|
+
/** The canonical (realpath) git common dir the id is derived from. */
|
|
84
|
+
commonDir: string;
|
|
85
|
+
registeredAt: string;
|
|
86
|
+
}
|
|
87
|
+
/** A project service's ownership lease (`service.ts`) — who authoritatively owns the service now,
|
|
88
|
+
* persisted independently of any runtime so ownership survives the runtime that held it. */
|
|
89
|
+
interface ServiceLease {
|
|
90
|
+
project: string;
|
|
91
|
+
service: string;
|
|
92
|
+
/** The service endpoint's record id — the durable mailbox and the stable reference. */
|
|
93
|
+
endpoint: string;
|
|
94
|
+
/** Fencing generation: bumped on every change of authority (a new reservation or a handoff), so a
|
|
95
|
+
* runtime holding an older generation is provably stale. */
|
|
96
|
+
generation: number;
|
|
97
|
+
/** `vacant` — no owner, no start in progress. `reserved` — a start is in progress (or failed and
|
|
98
|
+
* not yet expired). `active` — `holder` owns the service at `generation`. */
|
|
99
|
+
state: 'vacant' | 'reserved' | 'active';
|
|
100
|
+
holder?: string;
|
|
101
|
+
reservation?: {
|
|
102
|
+
token: string;
|
|
103
|
+
by?: string;
|
|
104
|
+
at: string;
|
|
105
|
+
expiresAt: string;
|
|
106
|
+
forced?: boolean;
|
|
107
|
+
};
|
|
108
|
+
updatedAt: string;
|
|
109
|
+
}
|
|
67
110
|
interface InboxSnapshot {
|
|
68
111
|
unread: Message[];
|
|
69
112
|
read: Message[];
|
|
@@ -89,6 +132,14 @@ interface Store {
|
|
|
89
132
|
listAgents(): AgentRecord[];
|
|
90
133
|
removeAgent(id: string): void;
|
|
91
134
|
removeAgentData(id: string): void;
|
|
135
|
+
/** Upsert a project record (keyed by `rec.id`). */
|
|
136
|
+
putProject(rec: ProjectRecord): void;
|
|
137
|
+
getProject(id: string): ProjectRecord | undefined;
|
|
138
|
+
listProjects(): ProjectRecord[];
|
|
139
|
+
/** Upsert a service lease (keyed by `lease.project` + `lease.service`). Callers mutate a lease only
|
|
140
|
+
* while holding that service's named lock (`service.ts`). */
|
|
141
|
+
putServiceLease(lease: ServiceLease): void;
|
|
142
|
+
getServiceLease(project: string, service: string): ServiceLease | undefined;
|
|
92
143
|
putPaneIndex(pane: string, agentId: string): void;
|
|
93
144
|
resolvePaneId(pane: string): string | undefined;
|
|
94
145
|
/** Reverse-lookup: an agent's pane from the index, when the agent record itself carries none
|
|
@@ -210,18 +261,33 @@ interface DecommissionInput {
|
|
|
210
261
|
id: string;
|
|
211
262
|
/** Discard uncommitted changes in the worktree. Never overrides refusing the primary checkout. */
|
|
212
263
|
force?: boolean;
|
|
264
|
+
/**
|
|
265
|
+
* Reap the unit but leave its worktree on disk, so a pool can detach it and hand it to the next
|
|
266
|
+
* unit — far cheaper than a fresh checkout per unit. Never overrides refusing the primary
|
|
267
|
+
* checkout: that guard is about identity, not destructiveness.
|
|
268
|
+
*/
|
|
269
|
+
keepWorktree?: boolean;
|
|
213
270
|
}
|
|
214
271
|
interface DecommissionResult {
|
|
215
272
|
agent: AgentRecord;
|
|
216
273
|
worktreeRoot?: string;
|
|
274
|
+
/**
|
|
275
|
+
* The worktree left on disk by `keepWorktree` — set only when a worktree was actually there to
|
|
276
|
+
* keep, so a pool manager can treat its presence as "this path is reusable" rather than
|
|
277
|
+
* re-checking `worktreeRoot` against disk.
|
|
278
|
+
*/
|
|
279
|
+
retainedWorktree?: string;
|
|
217
280
|
pane?: string;
|
|
218
281
|
}
|
|
219
282
|
/**
|
|
220
283
|
* Tear a unit down and reap its registry record — the deterministic inverse of `spawn`. Refuses
|
|
221
|
-
* the primary checkout (absolute — `--force`
|
|
222
|
-
* `--force`. Teardown always precedes reap: an already-gone worktree or pane
|
|
223
|
-
* genuine worktree-removal failure aborts and leaves the record intact so the
|
|
224
|
-
* retryable.
|
|
284
|
+
* the primary checkout (absolute — neither `--force` nor `--keep-worktree` overrides it) and a
|
|
285
|
+
* dirty worktree unless `--force`. Teardown always precedes reap: an already-gone worktree or pane
|
|
286
|
+
* is tolerated, but a genuine worktree-removal failure aborts and leaves the record intact so the
|
|
287
|
+
* operation is retryable.
|
|
288
|
+
*
|
|
289
|
+
* `keepWorktree` reaps everything else — pane, record, pane pointer, brief — and leaves the
|
|
290
|
+
* checkout on disk, reporting it as `retainedWorktree`.
|
|
225
291
|
*/
|
|
226
292
|
declare function decommission(ctx: IdContext, input: DecommissionInput): DecommissionResult;
|
|
227
293
|
//#endregion
|
|
@@ -372,6 +438,9 @@ declare const paths: {
|
|
|
372
438
|
dataDir: (root: string, id: string) => string;
|
|
373
439
|
briefFile: (root: string, id: string) => string;
|
|
374
440
|
mainPaneFile: (root: string) => string;
|
|
441
|
+
projectsDir: (root: string) => string;
|
|
442
|
+
serviceLeaseFile: (root: string, project: string, service: string) => string;
|
|
443
|
+
projectFile: (root: string, id: string) => string;
|
|
375
444
|
/** A message's file path within `toId`'s unread/read inbox dir, keyed by its own collision-free
|
|
376
445
|
* id — validated the same as an agent id (it's the same class of risk: a peer- or CLI-controlled
|
|
377
446
|
* string becoming a filename). */
|
|
@@ -379,6 +448,168 @@ declare const paths: {
|
|
|
379
448
|
messageReadFile: (root: string, toId: string, msgId: string) => string;
|
|
380
449
|
};
|
|
381
450
|
//#endregion
|
|
451
|
+
//#region src/project.d.ts
|
|
452
|
+
interface ProjectContext {
|
|
453
|
+
store: Store;
|
|
454
|
+
exec?: Exec;
|
|
455
|
+
now?: () => number;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Register (or idempotently refresh) the project containing `dir` — any checkout of it, default or
|
|
459
|
+
* linked. Keeps the first `registeredAt`, so re-registering from a worktree is a no-op in effect.
|
|
460
|
+
*/
|
|
461
|
+
declare function registerProject(ctx: ProjectContext, input?: {
|
|
462
|
+
dir?: string;
|
|
463
|
+
}): ProjectRecord;
|
|
464
|
+
declare function listProjects(store: Store): ProjectRecord[];
|
|
465
|
+
/**
|
|
466
|
+
* Resolve a project from anywhere — by id, by a path inside any of its checkouts, or by name when
|
|
467
|
+
* exactly one registered project carries it. A path registers its project on first use: git has
|
|
468
|
+
* already confirmed it is a repository, and registering is idempotent, so there is nothing a typo
|
|
469
|
+
* could mint. A name never registers — nothing can be found by a name no checkout has reported —
|
|
470
|
+
* and an ambiguous name names its candidates rather than picking one.
|
|
471
|
+
*/
|
|
472
|
+
declare function resolveProject(ctx: ProjectContext, ref: string): ProjectRecord;
|
|
473
|
+
//#endregion
|
|
474
|
+
//#region src/service.d.ts
|
|
475
|
+
interface ServiceContext extends IdContext {
|
|
476
|
+
/** Whether a unit's session is still there. Defaults to asking its multiplexer (`sessionLive`). */
|
|
477
|
+
isLive?: (unit: AgentRecord) => boolean;
|
|
478
|
+
}
|
|
479
|
+
type ServiceHealth = 'vacant' | 'starting' | 'expired' | 'healthy' | 'unhealthy';
|
|
480
|
+
interface ServiceView {
|
|
481
|
+
project: ProjectRecord;
|
|
482
|
+
endpoint: AgentRecord;
|
|
483
|
+
lease: ServiceLease;
|
|
484
|
+
/** The owning unit's record, when the lease is active and the record still exists. */
|
|
485
|
+
owner?: AgentRecord;
|
|
486
|
+
health: ServiceHealth;
|
|
487
|
+
/** Whether the owner's session can be controlled (focused, nudged, read) — `pane` when it has a
|
|
488
|
+
* multiplexer pane, `none` otherwise. Resolving the address never implies control. */
|
|
489
|
+
control: 'pane' | 'none';
|
|
490
|
+
/** A human-readable qualification of `health`/`control`, when there is one. */
|
|
491
|
+
note?: string;
|
|
492
|
+
}
|
|
493
|
+
type AcquireResult = {
|
|
494
|
+
outcome: 'resolved';
|
|
495
|
+
lease: ServiceLease;
|
|
496
|
+
view: ServiceView;
|
|
497
|
+
} | {
|
|
498
|
+
outcome: 'starting';
|
|
499
|
+
lease: ServiceLease;
|
|
500
|
+
view: ServiceView;
|
|
501
|
+
} | {
|
|
502
|
+
outcome: 'reserved';
|
|
503
|
+
lease: ServiceLease;
|
|
504
|
+
view: ServiceView;
|
|
505
|
+
token: string;
|
|
506
|
+
};
|
|
507
|
+
type OwnershipErrorCode = 'stale' | 'unit-not-live' | 'unknown-unit';
|
|
508
|
+
/** A refused ownership transition. `stale` means the caller's generation, token, or holder claim is
|
|
509
|
+
* not the current one — the caller is not (or no longer) the authority it claims to be. */
|
|
510
|
+
declare class ServiceOwnershipError extends Error {
|
|
511
|
+
readonly code: OwnershipErrorCode;
|
|
512
|
+
constructor(code: OwnershipErrorCode, message: string);
|
|
513
|
+
}
|
|
514
|
+
/** How long a reservation holds before another caller may take over a start that never bound. */
|
|
515
|
+
declare const DEFAULT_RESERVATION_TTL_MS: number;
|
|
516
|
+
declare function serviceEndpointId(projectId: string, name: string): string;
|
|
517
|
+
/** Read a service's ownership without changing anything. Throws for a service never started. */
|
|
518
|
+
declare function resolveService(ctx: ServiceContext, projectRef: string, name: string): ServiceView;
|
|
519
|
+
interface AcquireInput {
|
|
520
|
+
/** Who is starting the service — recorded on the reservation for diagnosis. */
|
|
521
|
+
by?: string;
|
|
522
|
+
ttlMs?: number;
|
|
523
|
+
/** Replace even a healthy owner or an unexpired reservation. Must name the current generation, so
|
|
524
|
+
* a force decided on a stale read fails instead of displacing an owner the caller never saw. */
|
|
525
|
+
force?: {
|
|
526
|
+
generation: number;
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Contact-or-start: resolve the service's healthy owner, or reserve the right to start one. Exactly
|
|
531
|
+
* one of any number of concurrent callers gets `reserved`; the rest see `starting` (a start is in
|
|
532
|
+
* progress) or `resolved`. A vacant service, an unhealthy owner, and an expired reservation are all
|
|
533
|
+
* reservable, each under a new generation that fences out whatever held the old one.
|
|
534
|
+
*/
|
|
535
|
+
declare function acquireService(ctx: ServiceContext, projectRef: string, name: string, input?: AcquireInput): AcquireResult;
|
|
536
|
+
/**
|
|
537
|
+
* Complete a reservation: make `unit` the owner at the reserved generation. Refused when the
|
|
538
|
+
* reservation is no longer the current one (it expired and someone else re-reserved, or it was
|
|
539
|
+
* released) — the starter then holds a runtime that is not the authority and must stop it. A
|
|
540
|
+
* reservation that expired with nobody taking over is still bindable: nothing replaced it.
|
|
541
|
+
*/
|
|
542
|
+
declare function bindService(ctx: ServiceContext, projectRef: string, name: string, input: {
|
|
543
|
+
generation: number;
|
|
544
|
+
token: string;
|
|
545
|
+
unit: string;
|
|
546
|
+
}): ServiceView;
|
|
547
|
+
/**
|
|
548
|
+
* Give the service up: abandon a reservation (a failed start — pass its `token`) or step down as
|
|
549
|
+
* owner (pass the holder `unit`). Either must match the current generation. The generation is kept;
|
|
550
|
+
* the next acquire bumps it.
|
|
551
|
+
*/
|
|
552
|
+
declare function releaseService(ctx: ServiceContext, projectRef: string, name: string, input: {
|
|
553
|
+
generation: number;
|
|
554
|
+
token?: string;
|
|
555
|
+
unit?: string;
|
|
556
|
+
}): ServiceView;
|
|
557
|
+
/**
|
|
558
|
+
* Transfer ownership from the current holder to another live unit, under a new generation. Only the
|
|
559
|
+
* holder at the current generation can hand off, and afterwards it is stale.
|
|
560
|
+
*/
|
|
561
|
+
declare function handoffService(ctx: ServiceContext, projectRef: string, name: string, input: {
|
|
562
|
+
generation: number;
|
|
563
|
+
from: string;
|
|
564
|
+
to: string;
|
|
565
|
+
}): ServiceView;
|
|
566
|
+
/**
|
|
567
|
+
* The fencing check: succeed only when `unit` owns the service at exactly `generation`. A runtime
|
|
568
|
+
* calls this before acting as the service's authority; a stale runtime — replaced, handed off, or
|
|
569
|
+
* recovered past — is refused.
|
|
570
|
+
*/
|
|
571
|
+
declare function verifyOwnership(ctx: ServiceContext, projectRef: string, name: string, input: {
|
|
572
|
+
unit: string;
|
|
573
|
+
generation: number;
|
|
574
|
+
}): ServiceView;
|
|
575
|
+
/**
|
|
576
|
+
* Run `fn` as the verified owner, holding the service lock so no transition can interleave between
|
|
577
|
+
* the check and the act. For short critical sections only — the lock is bounded-wait for everyone
|
|
578
|
+
* else — and never reentrant: `fn` must not call another service transition on the same service.
|
|
579
|
+
*/
|
|
580
|
+
declare function withOwnership<T>(ctx: ServiceContext, projectRef: string, name: string, input: {
|
|
581
|
+
unit: string;
|
|
582
|
+
generation: number;
|
|
583
|
+
}, fn: (view: ServiceView) => T): T;
|
|
584
|
+
interface StartInput<T extends {
|
|
585
|
+
unit: string;
|
|
586
|
+
}> extends AcquireInput {
|
|
587
|
+
/** Launch the runtime that will own the service, returning its unit id. Called only by the one
|
|
588
|
+
* caller holding the reservation; `generation` is the generation that runtime will own at. */
|
|
589
|
+
launch: (reservation: {
|
|
590
|
+
generation: number;
|
|
591
|
+
endpoint: string;
|
|
592
|
+
}) => Promise<T>;
|
|
593
|
+
}
|
|
594
|
+
type StartResult<T> = {
|
|
595
|
+
outcome: 'resolved' | 'starting';
|
|
596
|
+
view: ServiceView;
|
|
597
|
+
} | {
|
|
598
|
+
outcome: 'started';
|
|
599
|
+
view: ServiceView;
|
|
600
|
+
launched: T;
|
|
601
|
+
};
|
|
602
|
+
/**
|
|
603
|
+
* Resolve-or-start end to end: acquire, and only when this caller wins the reservation, launch a
|
|
604
|
+
* runtime and bind it. A launch that throws releases the reservation (best-effort — an expiry covers
|
|
605
|
+
* a release that itself fails) so the start is retryable at once. A launch that finishes after its
|
|
606
|
+
* reservation was superseded is refused at bind: the launched unit is not the owner, and the error
|
|
607
|
+
* names it so the caller can stop it.
|
|
608
|
+
*/
|
|
609
|
+
declare function startService<T extends {
|
|
610
|
+
unit: string;
|
|
611
|
+
}>(ctx: ServiceContext, projectRef: string, name: string, input: StartInput<T>): Promise<StartResult<T>>;
|
|
612
|
+
//#endregion
|
|
382
613
|
//#region src/session.d.ts
|
|
383
614
|
interface SpawnInput {
|
|
384
615
|
harness?: string;
|
|
@@ -458,6 +689,11 @@ declare class FileStore implements Store {
|
|
|
458
689
|
listAgents(): AgentRecord[];
|
|
459
690
|
removeAgent(id: string): void;
|
|
460
691
|
removeAgentData(id: string): void;
|
|
692
|
+
putProject(rec: ProjectRecord): void;
|
|
693
|
+
getProject(id: string): ProjectRecord | undefined;
|
|
694
|
+
listProjects(): ProjectRecord[];
|
|
695
|
+
putServiceLease(lease: ServiceLease): void;
|
|
696
|
+
getServiceLease(project: string, service: string): ServiceLease | undefined;
|
|
461
697
|
putPaneIndex(pane: string, agentId: string): void;
|
|
462
698
|
resolvePaneId(pane: string): string | undefined;
|
|
463
699
|
findPaneByAgentId(agentId: string): string | undefined;
|
|
@@ -521,4 +757,4 @@ interface WatchOptions {
|
|
|
521
757
|
*/
|
|
522
758
|
declare function watchMail(ctx: MsgContext, input: WatchInput, onMessage: (msg: InboxItem) => void, opts?: WatchOptions): Promise<void>;
|
|
523
759
|
//#endregion
|
|
524
|
-
export { type AgentDef, type AgentRecord, type AwaitInput, type AwaitOutcome, type DecommissionInput, type DecommissionResult, type Exec, FileStore, type Format, type Harness, type HookEvent, type IdContext, type InboxItem, type InboxQuery, type InboxSnapshot, type Message, type MuxProbe, type RegisterInput, type RootOptions, type SendInput, type SpawnInput, type SpawnResult, type Store, ack, assertDistinctFromPrimary, awaitReply, bumpLastSeen, decommission, deleteMessage, detectHarness, emit, ensureMarker, fail, gitWorktreeAdapter, inbox, injectInbox, install, listAgentDefs, listAgents, loadAgent, nextStep, paths, peek, probeMultiplexer, projectRoot, prune, realExec, realizeLaunch, register, resolveAgent, resolveAgentDef, resolveBody, resolvePrimaryRoot, resolveProjectLocalRoot, resolveRecipient, resolveRoot, resolveSelfId, selectSessionAdapter, send, spawn, spawnAndWake, toonList, toonObject, touch, watchMail };
|
|
760
|
+
export { type AcquireInput, type AcquireResult, type AgentDef, type AgentRecord, type AwaitInput, type AwaitOutcome, DEFAULT_RESERVATION_TTL_MS, type DecommissionInput, type DecommissionResult, type Exec, FileStore, type Format, type Harness, type HookEvent, type IdContext, type InboxItem, type InboxQuery, type InboxSnapshot, type Message, type MuxProbe, type OwnershipErrorCode, type ProjectContext, type ProjectRecord, type RegisterInput, type RootOptions, type SendInput, type ServiceContext, type ServiceHealth, type ServiceLease, ServiceOwnershipError, type ServiceView, type SpawnInput, type SpawnResult, type StartInput, type StartResult, type Store, ack, acquireService, assertDistinctFromPrimary, awaitReply, bindService, bumpLastSeen, decommission, deleteMessage, detectHarness, emit, ensureMarker, fail, gitWorktreeAdapter, handoffService, inbox, injectInbox, install, listAgentDefs, listAgents, listProjects, loadAgent, nextStep, paths, peek, probeMultiplexer, projectRoot, prune, realExec, realizeLaunch, register, registerProject, releaseService, resolveAgent, resolveAgentDef, resolveBody, resolvePrimaryRoot, resolveProject, resolveProjectLocalRoot, resolveRecipient, resolveRoot, resolveSelfId, resolveService, selectSessionAdapter, send, serviceEndpointId, spawn, spawnAndWake, startService, toonList, toonObject, touch, verifyOwnership, watchMail, withOwnership };
|