@cotal-ai/cli 0.148.2 → 0.149.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.
@@ -3,11 +3,12 @@ import { createHash } from "node:crypto";
3
3
  import { readFileSync, rmSync, statSync } from "node:fs";
4
4
  import { hostname } from "node:os";
5
5
  import { resolve as resolvePath } from "node:path";
6
- import { assertValidRole, agentFilePath, AgentGrantConflictError, connectorServers, spawnEnvAllow, deprovisionAgent, discardLaunchArtifacts, firstFreeName, isReachable, reclaimWithChild, loadCotalConfig, modelPolicyRefusal, mintCreds, newIdentity, DEV_OWNER, parseShareSelection, principalKey, assertLifecycleToken, mintLifecycleUid, MANAGED_HANDOFF_FILE_ENV, parseManagedLifecycleHandoff, spawnNameError, provisionAgent, provisionAgentDurables, registry, resolveAuthProvider, resolveReadAcl, runAgentBearer, CotalEndpoint, GOAL_ENTITY_RUNNING_DETAIL_KIND, EP_UNDECLARED_ARG, seatCapFrom, acquireLock, inspectLock, localConsumerClaimPath, transferBucket, writeTransfer, peerLabel, } from "@cotal-ai/core";
6
+ import { assertValidRole, agentFilePath, AgentGrantConflictError, connectorServers, spawnEnvAllow, deprovisionAgent, discardLaunchArtifacts, firstFreeName, isReachable, reclaimWithChild, loadCotalConfig, modelPolicyRefusal, mintCreds, newIdentity, DEV_OWNER, parseShareSelection, principalKey, assertLifecycleToken, mintLifecycleUid, MANAGED_HANDOFF_FILE_ENV, parseManagedLifecycleHandoff, spawnNameError, provisionAgent, provisionAgentDurables, registry, resolveAuthProvider, resolveReadAcl, runAgentBearer, GOAL_ENTITY_RUNNING_DETAIL_KIND, EP_UNDECLARED_ARG, seatCapFrom, acquireLock, inspectLock, localConsumerClaimPath, transferBucket, writeTransfer, peerLabel, attemptCleanup, describeCleanupFailures, rejectionText, } from "@cotal-ai/core";
7
7
  import { parseCommaList, CommandFailure, loadLaunchPersonaFile, personaFileReference, PersonaSourceError, inputDisabledByFlag, jsonFlag, plainFlag, presentationOf, recordOperationWarning, renderReport, terminalWidth, wrapWords, writeResult, agentActorTokenKey, agentLifecycleSecretFilePaths, agentSecretFilePaths, agentSecretKeyForFile, agentSentinelCredsKey, authDir, cachedPreferences, credsFlag, launchFlags, findMesh, getSpaceAuth, loadManagerInstanceIdentity, materializeSecretToFile, mergeLaunchOptions, parseLaunchOptions, preflightOrThrow, provenance, resolveAgentType, resolvePreference, resolveTargetOrThrow, serverFlag, spaceAccountPath, spaceFlag, spaceKey, userAuthStateDir, workspaceSecretStore, refreshRegistrationPolicy, resolveSeatControlTarget, connectUserControlOrExit, userViewAuth, oneLine, codeSpan, DEFAULT_CONNECTOR, hintedConnector, manifestExtensionNames, OFFICIAL_CONNECTORS, recordOperationSpace, resolveMeshTarget, activateTerminalSeat, issueTerminalSeat, retireTerminalSeat, seatExpiryAt, resolveControlTarget, } from "@cotal-ai/workspace";
8
8
  import { readManagedLaunch, requestManagedLaunch } from "@cotal-ai/workspace/launch";
9
9
  import { c, stderrLine } from "../ui.js";
10
10
  import { liveMeshNames } from "../lib/live-names.js";
11
+ import { withProvisioner } from "../lib/provisioner.js";
11
12
  import { displayCmd } from "../lib/self-exec.js";
12
13
  import { entryScript } from "../seed/paths.js";
13
14
  import { completedFlagValue, completingFlagValue, hasCompletedFlagValue, positionalsForCompletion } from "../lib/completion.js";
@@ -586,7 +587,8 @@ export async function launchManagedPersona(values, positionals, events, launchOp
586
587
  // Without a flag the harness is the persona pin or a default, which only the target manager
587
588
  // can read: ask it, so the carry looks with the connector the launch runs (#2899).
588
589
  const agent = values.agent ?? await launchAgentOrExit(t, on, { name: ref, config: managerConfigRef, defaultAgent });
589
- const claim = await carryTranscriptOrExit(flags, on, values.resume, agent);
590
+ // The carry and its transfer-writer view stay on the mesh selected for this launch.
591
+ const claim = await carryTranscriptOrExit({ ...flags, space: t.space, server: t.server }, on, values.resume, agent);
590
592
  if (claim !== undefined)
591
593
  resumeClaim = { claim, agent };
592
594
  }
@@ -1596,49 +1598,36 @@ export async function spawn(args) {
1596
1598
  // channel rather than two.
1597
1599
  if (launchEvents)
1598
1600
  allowPublish = [...(allowPublish ?? []), connector.eventChannel({ owner: DEV_OWNER, actor: identity.id })];
1599
- const prov = new CotalEndpoint({
1600
- space,
1601
- servers: server,
1602
- creds: await mintCreds(auth, newIdentity(), "provisioner"),
1603
- channels: [],
1604
- consume: false,
1605
- registerPresence: false,
1606
- watchPresence: false,
1607
- watchChannels: false,
1608
- card: { name: "spawn-provisioner", role: "provisioner", kind: "endpoint" },
1609
- });
1610
- const reportStaticProvisioner = (e) => console.error(`! provisioner: ${e.message}`);
1611
- prov.on("error", reportStaticProvisioner);
1612
- prov.on("warning", reportStaticProvisioner);
1613
- await prov.start();
1614
1601
  // Foreground provisions the SAME durable footprint as `--detach` (DM/DLV durables + read-ACL
1615
1602
  // row): the daemon authorizes durable joins off the ACL row and leave is agent self-service, so
1616
1603
  // no managing host is required — and a silently live-only foreground agent permanently loses
1617
1604
  // every channel message posted while its connection blips (reconnect deliberately re-opens the
1618
1605
  // core-subs without re-backfill). `--live-only` opts back out; on a mesh with no delivery
1619
- // daemon the boot join itself reports live-only, ACL row or not.
1606
+ // daemon the boot join itself reports live-only, ACL row or not. A failed provisioner stop
1607
+ // never reaches this catch, so it cannot retire a credential the provisioning already minted.
1620
1608
  try {
1621
- // The seat's lifecycle is activated first, so the issuance can name its gate as the source of
1622
- // the evidence a helper's spawn is bounded by.
1623
- const seatTarget = { server, space, tlsRequired: target.tlsRequired, auth };
1624
- seat = { actor: identity.id, alias: name, lifecycleUid };
1625
- await activateTerminalSeat(seatTarget, seat);
1626
- const creds = await issueTerminalSeat(seatTarget, seat, seatExpiry(values, true), (issue) => {
1627
- acceptedToken = issue.issued.acceptedToken;
1628
- return provisionAgent(prov, auth, identity, {
1629
- subscribe,
1630
- allowSubscribe,
1631
- allowPublish,
1632
- role,
1633
- capabilities: def.capabilities,
1634
- ...(values["live-only"] ? { durableMembership: false } : {}),
1635
- lifecycleUid,
1636
- expiresAt: issue.expiresAt,
1637
- issued: issue.issued,
1638
- issuance: issue.issuance,
1609
+ const creds = await withProvisioner({ space, servers: server, auth }, "spawn-provisioner", async (prov) => {
1610
+ // The seat's lifecycle is activated first, so the issuance can name its gate as the source of
1611
+ // the evidence a helper's spawn is bounded by.
1612
+ const seatTarget = { server, space, tlsRequired: target.tlsRequired, auth };
1613
+ seat = { actor: identity.id, alias: name, lifecycleUid };
1614
+ await activateTerminalSeat(seatTarget, seat);
1615
+ return issueTerminalSeat(seatTarget, seat, seatExpiry(values, true), (issue) => {
1616
+ acceptedToken = issue.issued.acceptedToken;
1617
+ return provisionAgent(prov, auth, identity, {
1618
+ subscribe,
1619
+ allowSubscribe,
1620
+ allowPublish,
1621
+ role,
1622
+ capabilities: def.capabilities,
1623
+ ...(values["live-only"] ? { durableMembership: false } : {}),
1624
+ lifecycleUid,
1625
+ expiresAt: issue.expiresAt,
1626
+ issued: issue.issued,
1627
+ issuance: issue.issuance,
1628
+ });
1639
1629
  });
1640
1630
  });
1641
- await prov.stop();
1642
1631
  // Store first (the source of truth), then materialize — the child's launch reads this FILE.
1643
1632
  // The CLI is the local FS composition (byte-identical), same posture as the manager's spawn.
1644
1633
  const secrets = workspaceSecretStore(target.root);
@@ -1797,31 +1786,6 @@ function checkRemoteAgentMaterial(v, actor) {
1797
1786
  },
1798
1787
  };
1799
1788
  }
1800
- /** A caught value's text for a refusal. A provider or store may reject with any value, including null
1801
- * or one whose `message` getter or `toString` throws, and the refusal must still be printed, so the
1802
- * coercion to text runs inside the guard. */
1803
- function rejectionText(e) {
1804
- try {
1805
- return String(e?.message ?? e);
1806
- }
1807
- catch {
1808
- return "an unreadable rejection";
1809
- }
1810
- }
1811
- /** Run one cleanup step, recording its failure in `failed`. A step that threw would otherwise skip
1812
- * every step after it, and a swallowed one would leave nothing to report. */
1813
- async function attemptCleanup(failed, step, run) {
1814
- try {
1815
- await run();
1816
- }
1817
- catch (err) {
1818
- failed.push({ step, err });
1819
- }
1820
- }
1821
- /** `<step>: <reason>` for each failure. */
1822
- function describeCleanupFailures(failed) {
1823
- return failed.map(({ step, err }) => `${step}: ${rejectionText(err)}`).join("; ");
1824
- }
1825
1789
  /** Shred a user-mode agent's local material: both secrets from the store, then their files and the
1826
1790
  * health file. Each step is attempted and a failed one is recorded in `failed`. */
1827
1791
  async function shredAgentMaterial(failed, store, space, name, composition, paths) {
@@ -2070,34 +2034,15 @@ async function provisionUserForeground(target, name, ref, opts) {
2070
2034
  custody: { kind: "foreground" },
2071
2035
  });
2072
2036
  ownedToken = grant.actorToken;
2073
- const prov = new CotalEndpoint({
2074
- space,
2075
- servers: server,
2076
- creds: await mintCreds(infra, newIdentity(), "provisioner"),
2077
- channels: [],
2078
- consume: false,
2079
- registerPresence: false,
2080
- watchPresence: false,
2081
- watchChannels: false,
2082
- card: { name: "spawn-provisioner", role: "provisioner", kind: "endpoint" },
2083
- });
2084
- const reportUserProvisioner = (e) => console.error(`! provisioner: ${e.message}`);
2085
- prov.on("error", reportUserProvisioner);
2086
- prov.on("warning", reportUserProvisioner);
2087
- await prov.start();
2088
- try {
2089
- // Full durable footprint, same as the static foreground path: the ACL row is what lets the
2090
- // delivery daemon authorize this agent's durable joins. `--live-only` opts out.
2091
- await provisionAgentDurables(prov, { owner, actor: name, lifecycleUid: opts.lifecycleUid }, {
2092
- subscribe: opts.subscribe,
2093
- allowSubscribe: opts.allowSubscribe,
2094
- role: opts.role,
2095
- ...(opts.liveOnly ? { durableMembership: false } : {}),
2096
- });
2097
- }
2098
- finally {
2099
- await prov.stop();
2100
- }
2037
+ await withProvisioner({ space, servers: server, auth: infra }, "spawn-provisioner", (prov) =>
2038
+ // Full durable footprint, same as the static foreground path: the ACL row is what lets the
2039
+ // delivery daemon authorize this agent's durable joins. `--live-only` opts out.
2040
+ provisionAgentDurables(prov, { owner, actor: name, lifecycleUid: opts.lifecycleUid }, {
2041
+ subscribe: opts.subscribe,
2042
+ allowSubscribe: opts.allowSubscribe,
2043
+ role: opts.role,
2044
+ ...(opts.liveOnly ? { durableMembership: false } : {}),
2045
+ }));
2101
2046
  // The store holds the source of truth; the bearer re-exec (`--token-file`) and the launch's
2102
2047
  // sentinel handoff read FILES — materialize both at the canonical paths (byte-identical
2103
2048
  // rewrites under this, the local FS, composition).