open-tag 0.0.3 → 0.0.4

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.
@@ -5522,7 +5522,7 @@ function date(params) {
5522
5522
  return /* @__PURE__ */ _coercedDate(ZodDate, params);
5523
5523
  }
5524
5524
  //#endregion
5525
- //#region ../../packages/shared/dist/task-DaUY_qJd.mjs
5525
+ //#region ../../packages/shared/dist/task-C8jjVDIV.mjs
5526
5526
  const RUNTIME_INSTRUCTIONS_MAX_BYTES = 24576;
5527
5527
  const RUNTIME_DEFAULT_MAX_DURATION_MS = 18e5;
5528
5528
  const RUNTIME_MAX_DURATION_MS = 864e5;
@@ -5901,7 +5901,35 @@ const PROVIDER_CLI_VALIDATION_RETRY_REASONS = [
5901
5901
  ];
5902
5902
  _enum(PROVIDER_CLI_VALIDATION_RETRY_REASONS);
5903
5903
  const ProviderCliValidationResultReasonSchema = _enum([...INTEGRATION_CREDENTIAL_EXECUTION_REASONS, ...PROVIDER_CLI_VALIDATION_RETRY_REASONS]);
5904
+ /** Allowlisted artifact failure reasons that may leave the machine. */
5905
+ const PROVIDER_CLI_ARTIFACT_PUBLIC_REASONS = [
5906
+ "unsupported_platform",
5907
+ "global_bin_unavailable",
5908
+ "integrity_failed",
5909
+ "version_incompatible"
5910
+ ];
5911
+ const ProviderCliArtifactPublicReasonSchema = _enum(PROVIDER_CLI_ARTIFACT_PUBLIC_REASONS);
5912
+ const ProviderCliPublicFailureReasonSchema = _enum([...INTEGRATION_CREDENTIAL_EXECUTION_REASONS, ...PROVIDER_CLI_ARTIFACT_PUBLIC_REASONS]);
5904
5913
  const PROVIDER_READINESS_V1_HEADER = "x-opentag-provider-readiness";
5914
+ const PROVIDER_CLI_REASON_V2_HEADER = "x-opentag-provider-cli-reason";
5915
+ const PROVIDER_CLI_ALWAYS_MANUAL_ARTIFACT_REASONS = /* @__PURE__ */ new Set([
5916
+ "unsupported_platform",
5917
+ "global_bin_unavailable",
5918
+ "integrity_failed"
5919
+ ]);
5920
+ /** Map a local diagnostic code onto the public artifact allowlist. Unknown codes stay unpublished. */
5921
+ function publicProviderCliArtifactReason(code) {
5922
+ if (code === "unsupported_platform" || code === "global_bin_unavailable" || code === "integrity_failed" || code === "version_incompatible") return code;
5923
+ }
5924
+ /**
5925
+ * Manual failures need a user or release change. `version_incompatible` is manual only after a
5926
+ * final ensure: an inspection may still be repaired by installing a supported managed artifact.
5927
+ */
5928
+ function providerCliArtifactFailureIsManual(input) {
5929
+ if (!input.reason) return false;
5930
+ if (PROVIDER_CLI_ALWAYS_MANUAL_ARTIFACT_REASONS.has(input.reason)) return true;
5931
+ return input.reason === "version_incompatible" && input.stage === "ensure";
5932
+ }
5905
5933
  const ComputerConnectCodeModeSchema = _enum(["create", "repair"]);
5906
5934
  /**
5907
5935
  * Exchange identifies the local installation, not an existing Computer. The Server decides create vs
@@ -6039,7 +6067,8 @@ const ComputerProviderReadinessCollectionSchema = array(object({
6039
6067
  const ComputerImCliReadinessCollectionSchema = array(object({
6040
6068
  provider: ImCliProviderSchema,
6041
6069
  status: ImCliReadinessStatusSchema,
6042
- observedAt: string().datetime().nullable()
6070
+ observedAt: string().datetime().nullable(),
6071
+ reason: ProviderCliArtifactPublicReasonSchema.optional()
6043
6072
  }).strict()).max(IM_CLI_PROVIDERS.length).superRefine((observations, context) => {
6044
6073
  const seen = /* @__PURE__ */ new Set();
6045
6074
  for (const [index, observation] of observations.entries()) {
@@ -6259,7 +6288,7 @@ const ProviderCliHandoffProgressSchema = object({
6259
6288
  "checking_credentials",
6260
6289
  "needs_attention"
6261
6290
  ]),
6262
- reason: IntegrationCredentialExecutionReasonSchema.optional()
6291
+ reason: ProviderCliPublicFailureReasonSchema.optional()
6263
6292
  }).strict();
6264
6293
  /**
6265
6294
  * The machine-readable identity a cross-Provider messaging start fails with: unbind this exact current binding,
@@ -6406,6 +6435,7 @@ const ImBindingDiagnosticsSchema = object({
6406
6435
  "ready",
6407
6436
  "unavailable"
6408
6437
  ]),
6438
+ providerCliReason: ProviderCliArtifactPublicReasonSchema.optional(),
6409
6439
  credentialExecutionReadiness: IntegrationCredentialExecutionStatusSchema,
6410
6440
  credentialExecutionReason: IntegrationCredentialExecutionReasonSchema.optional(),
6411
6441
  credentialGeneration: number().int().min(0),
@@ -7457,7 +7487,7 @@ const RUNTIME_CLIENT_CAPABILITY_OFFERS = {
7457
7487
  },
7458
7488
  [RUNTIME_CAPABILITY.providerCliReconcile]: {
7459
7489
  min: 1,
7460
- max: 1
7490
+ max: 2
7461
7491
  },
7462
7492
  [RUNTIME_CAPABILITY.sessionCollaboration]: {
7463
7493
  min: 2,
@@ -9307,8 +9337,15 @@ const ProviderCliArtifactStatusFrameSchema = object({
9307
9337
  "checking",
9308
9338
  "ready",
9309
9339
  "unavailable"
9310
- ])
9311
- }).strict();
9340
+ ]),
9341
+ reason: ProviderCliArtifactPublicReasonSchema.optional()
9342
+ }).strict().superRefine((frame, context) => {
9343
+ if (frame.status !== "unavailable" && frame.reason !== void 0) context.addIssue({
9344
+ code: "custom",
9345
+ path: ["reason"],
9346
+ message: "An artifact reason is only valid on unavailable"
9347
+ });
9348
+ });
9312
9349
  const IntegrationCliValidationGrantMaterialSchema = discriminatedUnion("provider", [object({
9313
9350
  provider: literal("feishu"),
9314
9351
  appId: runtimeByteString(512, "Feishu App ID exceeds the 512-byte limit", 1),
@@ -19611,7 +19648,8 @@ var OpenTagApi = class {
19611
19648
  listAccountComputers(accessToken, options) {
19612
19649
  return this.#request(HTTP_PATHS.accountComputers, ListAccountComputersResponseSchema, { headers: {
19613
19650
  authorization: `Bearer ${accessToken}`,
19614
- [PROVIDER_READINESS_V1_HEADER]: "1"
19651
+ [PROVIDER_READINESS_V1_HEADER]: "1",
19652
+ [PROVIDER_CLI_REASON_V2_HEADER]: "2"
19615
19653
  } }, options);
19616
19654
  }
19617
19655
  createAgent(accessToken, input, options) {
@@ -19720,7 +19758,10 @@ var OpenTagApi = class {
19720
19758
  }, options);
19721
19759
  }
19722
19760
  getImBindingDiagnostics(accessToken, imBindingId, options) {
19723
- return this.#request(imBindingDiagnosticsPath(imBindingId), ImBindingDiagnosticsSchema, { headers: { authorization: `Bearer ${accessToken}` } }, options);
19761
+ return this.#request(imBindingDiagnosticsPath(imBindingId), ImBindingDiagnosticsSchema, { headers: {
19762
+ authorization: `Bearer ${accessToken}`,
19763
+ [PROVIDER_CLI_REASON_V2_HEADER]: "2"
19764
+ } }, options);
19724
19765
  }
19725
19766
  disableImBinding(accessToken, imBindingId, options) {
19726
19767
  return this.#requestNoContent(imBindingDisablePath(imBindingId), {
@@ -19887,7 +19928,8 @@ function resolveOpenTagHomeLayout(home = resolveOpenTagHome()) {
19887
19928
  return {
19888
19929
  config,
19889
19930
  contextTreeBin: join(resolvedHome, "context-tree", "bin"),
19890
- contextTreeConfigFile: join(config, "context-tree.json"),
19931
+ contextTreeConfigDir: join(config, "context-tree"),
19932
+ contextTreeConfigFile: join(config, "context-tree", "config.json"),
19891
19933
  contextTreePreparationFile: join(state, "context-tree-preparation.json"),
19892
19934
  daemonState: join(state, "daemon"),
19893
19935
  data,
@@ -20336,6 +20378,16 @@ async function checkServerHealth(serverUrl, fetchImpl = fetch, timeoutMs = SERVE
20336
20378
  clearTimeout(timeout);
20337
20379
  }
20338
20380
  }
20381
+ /** Compose all environment layers before giving Session tools first place on PATH. */
20382
+ function composeRuntimeEnvironment(environment, workspaceEnvironment, pathPrepend) {
20383
+ const composed = {
20384
+ ...environment,
20385
+ ...workspaceEnvironment
20386
+ };
20387
+ const current = composed.PATH;
20388
+ if (pathPrepend && current !== pathPrepend && !current?.startsWith(`${pathPrepend}${delimiter}`)) composed.PATH = current ? `${pathPrepend}${delimiter}${current}` : pathPrepend;
20389
+ return composed;
20390
+ }
20339
20391
  /**
20340
20392
  * Spawn errnos that mean "the machine could not run the check right now", not
20341
20393
  * "the binary is broken or absent". These map to `temporarily_unavailable` and
@@ -21431,14 +21483,11 @@ var ClaudeCodeAgentRuntimeFactory = class {
21431
21483
  const environment = claudeCodeAgentRuntimeEnvironment(options.process?.env);
21432
21484
  const command = options.process?.command ?? "claude";
21433
21485
  const prefix = options.process?.args ?? [];
21434
- this.#createProcess = options.createProcess ?? ((cwd, args, workspaceEnvironment) => new ClaudeCodeProcess({
21486
+ this.#createProcess = options.createProcess ?? ((cwd, args, workspaceEnvironment, pathPrepend) => new ClaudeCodeProcess({
21435
21487
  command,
21436
21488
  args: [...prefix, ...args],
21437
21489
  cwd,
21438
- env: {
21439
- ...environment,
21440
- ...workspaceEnvironment
21441
- },
21490
+ env: composeRuntimeEnvironment(environment, workspaceEnvironment, pathPrepend),
21442
21491
  maxLineBytes: options.process?.maxLineBytes,
21443
21492
  maxStderrBytes: options.process?.maxStderrBytes,
21444
21493
  spawnProcess: options.process?.spawnProcess
@@ -21508,7 +21557,7 @@ var ClaudeCodeAgentRuntimeFactory = class {
21508
21557
  return new ClaudeCodeAgentRuntime({
21509
21558
  binding,
21510
21559
  configuration: request.configuration,
21511
- createProcess: (args) => this.#createProcess(request.workspace.cwd, args, request.workspace.environment),
21560
+ createProcess: (args) => this.#createProcess(request.workspace.cwd, args, request.workspace.environment, request.workspace.pathPrepend),
21512
21561
  emptyNativeToolAllowList: isEmptyNativeToolAllowList(request.policy),
21513
21562
  eventSink: request.eventSink,
21514
21563
  hostedTools: request.hostedTools,
@@ -22935,21 +22984,18 @@ var CodexAgentRuntimeFactory = class {
22935
22984
  this.#clientVersion = options.clientVersion;
22936
22985
  const environment = options.process?.env ?? codexAgentRuntimeEnvironment();
22937
22986
  const command = options.process?.command ?? "codex";
22938
- const createDefaultClient = (cwd, workspaceEnvironment, expectedCodexHome = options.process?.expectedCodexHome) => new CodexAppServerProcess({
22987
+ const createDefaultClient = (cwd, workspaceEnvironment, pathPrepend, expectedCodexHome = options.process?.expectedCodexHome) => new CodexAppServerProcess({
22939
22988
  command,
22940
22989
  args: options.process?.args ?? [...CODEX_AGENT_RUNTIME_APP_SERVER_ARGS],
22941
22990
  cwd,
22942
- env: {
22943
- ...environment,
22944
- ...workspaceEnvironment
22945
- },
22991
+ env: composeRuntimeEnvironment(environment, workspaceEnvironment, pathPrepend),
22946
22992
  expectedCodexHome,
22947
22993
  maxLineBytes: options.process?.maxLineBytes,
22948
22994
  requestTimeoutMs: options.process?.requestTimeoutMs,
22949
22995
  spawnProcess: options.process?.spawnProcess
22950
22996
  });
22951
22997
  this.#createClient = options.createClient ?? createDefaultClient;
22952
- const createProbeClient = options.createClient ?? ((cwd, probeEnvironment) => createDefaultClient(cwd, probeEnvironment, probeEnvironment?.CODEX_HOME));
22998
+ const createProbeClient = options.createClient ?? ((cwd, probeEnvironment) => createDefaultClient(cwd, probeEnvironment, void 0, probeEnvironment?.CODEX_HOME));
22953
22999
  this.#probeRunner = options.probeRunner ?? (async (signal) => {
22954
23000
  const local = await probeCodex(command, environment, signal);
22955
23001
  if (!local.credential) return {
@@ -23103,7 +23149,7 @@ var CodexAgentRuntimeFactory = class {
23103
23149
  const expectedThreadId = parsedBinding?.threadId;
23104
23150
  const requestedHostedToolsHash = request.hostedTools ? hostedToolsHash(request.hostedTools.definitions) : void 0;
23105
23151
  if (mode === "resume" && parsedBinding?.hostedToolsHash !== requestedHostedToolsHash) throw new AgentRuntimeError("binding_incompatible", "Codex binding hosted tools cannot change during exact resume");
23106
- const client = this.#createClient(request.workspace.cwd, request.workspace.environment);
23152
+ const client = this.#createClient(request.workspace.cwd, request.workspace.environment, request.workspace.pathPrepend);
23107
23153
  try {
23108
23154
  if (request.hostedTools && typeof client.setDynamicToolHandler !== "function") throw new AgentRuntimeError("configuration_invalid", "Codex App Server transport does not support hosted dynamic tools");
23109
23155
  await client.initialize(this.#clientVersion);
@@ -26555,6 +26601,7 @@ function renderAgentHome(agentHome) {
26555
26601
  "- `worktrees/<unique-task-key>/` — agent-managed checkouts for source access and code work. Concurrent code tasks each use a distinct worktree (and a distinct branch when editing). Keep later operations for the same task in its own worktree. No two code tasks edit one checkout.",
26556
26602
  "- `files/<unique-task-key>/` — non-repository task artifacts, created only when needed.",
26557
26603
  "",
26604
+ "OpenTag provides the bundled `context-tree` command. If it cannot run, report a runtime setup problem; do not install it globally.",
26558
26605
  "Context Tree stays the separately configured shared tree managed by its matching CLI and skills. When running Context Tree project commands from a task subdirectory, pass `--project-path \"<Agent Home>\"` to use the connected Home; do not create or reconnect a tree just because the task cwd changed. Follow the matching skill write protocol. Do not invent an independent Git policy for Tree writes.",
26559
26606
  ""
26560
26607
  ];
@@ -26623,7 +26670,6 @@ var SessionRuntimeManager = class {
26623
26670
  #proofManager;
26624
26671
  #slackConfigWritableRoot;
26625
26672
  #providerCliLaunchPath;
26626
- #inheritedPath;
26627
26673
  #workspace;
26628
26674
  #sessions = /* @__PURE__ */ new Map();
26629
26675
  #prepares = /* @__PURE__ */ new Set();
@@ -26638,11 +26684,10 @@ var SessionRuntimeManager = class {
26638
26684
  if (options.contextTree) this.#contextTree = options.contextTree;
26639
26685
  this.#ensureProviderReady = options.ensureProviderReady;
26640
26686
  this.#providers = options.providers;
26641
- this.#home = options.home ?? "";
26687
+ this.#home = options.home;
26642
26688
  this.#providerEnvironmentPath = options.providerEnvironmentPath;
26643
26689
  this.#slackConfigWritableRoot = options.slackConfigWritableRoot;
26644
26690
  this.#providerCliLaunchPath = options.providerCliLaunchPath;
26645
- this.#inheritedPath = options.inheritedPath ?? process.env.PATH;
26646
26691
  this.#proofManager = options.proofManager ?? {
26647
26692
  cleanup: async () => void 0,
26648
26693
  materialize: async () => {
@@ -26773,6 +26818,7 @@ var SessionRuntimeManager = class {
26773
26818
  await managed.eventSink?.(event);
26774
26819
  };
26775
26820
  const contextTree = await prepareContextTree(this.#contextTree, managed.cwd);
26821
+ const configurationRoots = await prepareConfigurationRoots(resolveOpenTagHomeLayout(this.#home));
26776
26822
  const common = {
26777
26823
  eventSink,
26778
26824
  systemPrompt: renderManagedSystemPrompt(managed.snapshot, {
@@ -26786,15 +26832,17 @@ var SessionRuntimeManager = class {
26786
26832
  }),
26787
26833
  workspace: {
26788
26834
  cwd: managed.cwd,
26835
+ ...visibleProviderCliPath(managed, this.#providerCliLaunchPath),
26789
26836
  environment: {
26790
26837
  ...this.#home ? { OPENTAG_HOME: this.#home } : {},
26791
26838
  ...managed.proofPath ? { OPENTAG_SESSION_PROOF_FILE: managed.proofPath } : {},
26792
- ...managed.sessionKind === "visible" ? {
26793
- OPENTAG_PROVIDER_ENV_FILE: this.#providerEnvironmentPath(managed.binding.sessionId),
26794
- ...visibleProviderCliPath(managed.binding.sessionId, this.#providerCliLaunchPath, this.#inheritedPath)
26795
- } : {}
26839
+ ...managed.sessionKind === "visible" ? { OPENTAG_PROVIDER_ENV_FILE: this.#providerEnvironmentPath(managed.binding.sessionId) } : {}
26796
26840
  },
26797
- writableRoots: [...visibleSlackWritableRoots(managed.sessionKind, managed.cwd, managed.binding.sessionId, this.#slackConfigWritableRoot), ...contextTree.writableRoots]
26841
+ writableRoots: [
26842
+ ...visibleSlackWritableRoots(managed.sessionKind, managed.cwd, managed.binding.sessionId, this.#slackConfigWritableRoot),
26843
+ ...configurationRoots,
26844
+ ...contextTree.writableRoots
26845
+ ]
26798
26846
  },
26799
26847
  policy: provider.policy(managed.snapshot),
26800
26848
  configuration: {
@@ -26915,13 +26963,6 @@ var SessionRuntimeManager = class {
26915
26963
  if (this.#closing) throw new Error("The Session Runtime manager is closing");
26916
26964
  }
26917
26965
  };
26918
- function visibleProviderCliPath(sessionId, resolveLaunchPath, inheritedPath, pathDelimiter = delimiter) {
26919
- const launchPath = resolveLaunchPath?.(sessionId);
26920
- if (!launchPath) return {};
26921
- if (!inheritedPath) return { PATH: launchPath };
26922
- if (inheritedPath === launchPath || inheritedPath.startsWith(`${launchPath}${pathDelimiter}`)) return { PATH: inheritedPath };
26923
- return { PATH: `${launchPath}${pathDelimiter}${inheritedPath}` };
26924
- }
26925
26966
  /**
26926
26967
  * Resolve Context Tree for one Agent Workspace into the two things a Provider Runtime needs.
26927
26968
  *
@@ -26963,6 +27004,21 @@ async function waitForStart(start, signal) {
26963
27004
  signal.removeEventListener("abort", onAbort);
26964
27005
  }
26965
27006
  }
27007
+ function visibleProviderCliPath(managed, resolveLaunchPath) {
27008
+ return managed.sessionKind === "visible" ? { pathPrepend: resolveLaunchPath?.(managed.binding.sessionId) } : {};
27009
+ }
27010
+ async function prepareConfigurationRoots(layout) {
27011
+ try {
27012
+ await mkdir(layout.contextTreeConfigDir, {
27013
+ mode: 448,
27014
+ recursive: true
27015
+ });
27016
+ return [layout.contextTreeConfigDir];
27017
+ } catch (error) {
27018
+ logger$13.warn({ code: error.code }, "Context Tree configuration directory could not be created");
27019
+ return [];
27020
+ }
27021
+ }
26966
27022
  var TurnTraceBuffer = class {
26967
27023
  #id;
26968
27024
  #maxBufferBytes;
@@ -28737,6 +28793,8 @@ var ContextTreeManager = class {
28737
28793
  #cooldown = /* @__PURE__ */ new Map();
28738
28794
  #inFlight = /* @__PURE__ */ new Map();
28739
28795
  #observedTarget = /* @__PURE__ */ new Map();
28796
+ #shimPreparation;
28797
+ #shimRetryAt = 0;
28740
28798
  #pending = Promise.resolve();
28741
28799
  constructor(options) {
28742
28800
  this.#home = resolve(options.home);
@@ -28762,8 +28820,15 @@ var ContextTreeManager = class {
28762
28820
  * connection from writing into a workspace still mid-migration.
28763
28821
  */
28764
28822
  async ensureAgent(cwd) {
28823
+ return this.#withinSessionStartBudget(this.#prepareAgent(cwd));
28824
+ }
28825
+ async #prepareAgent(cwd) {
28826
+ const executableFailure = !this.#package ? "PACKAGE_MISSING" : await this.#prepareShim() ? void 0 : "SHIM_UNAVAILABLE";
28765
28827
  const config = await this.readConfig();
28766
- if (!config) return { status: "unconfigured" };
28828
+ if (!config) return executableFailure ? {
28829
+ status: "unavailable",
28830
+ reason: executableFailure
28831
+ } : { status: "unconfigured" };
28767
28832
  const target = formatContextTreeTarget(config.target);
28768
28833
  if (this.#observedTarget.get(cwd) !== target) {
28769
28834
  this.#observedTarget.set(cwd, target);
@@ -28775,7 +28840,7 @@ var ContextTreeManager = class {
28775
28840
  const cooling = this.#cooldown.get(cwd);
28776
28841
  if (cooling?.target === target && cooling.until > Date.now()) return cooling.status;
28777
28842
  if (cooling) this.#cooldown.delete(cwd);
28778
- return this.#withinSessionStartBudget(this.#joinPreparation(cwd, config, target));
28843
+ return this.#joinPreparation(cwd, config, target, executableFailure);
28779
28844
  }
28780
28845
  async readConfig() {
28781
28846
  try {
@@ -28788,10 +28853,6 @@ var ContextTreeManager = class {
28788
28853
  async #ensureAgentOnce(cwd, config) {
28789
28854
  if (!this.#package) return this.#unavailable("PACKAGE_MISSING", config);
28790
28855
  if (!this.#codexHomeIsDefaultNamed) return this.#unavailable("CODEX_HOME_UNSUPPORTED", config);
28791
- if (!await this.#writeShim().catch((error) => {
28792
- this.#logger.warn({ err: describe(error) }, "Context Tree shim could not be created");
28793
- return false;
28794
- })) return this.#unavailable("SHIM_UNAVAILABLE", config);
28795
28856
  try {
28796
28857
  const treePath = (await this.#run(connectArguments(config.target, cwd), cwd, config.target.kind === "github")).tree?.path;
28797
28858
  if (typeof treePath !== "string" || treePath.length === 0) return this.#unavailable("CONNECT_FAILED", config);
@@ -28847,6 +28908,19 @@ var ContextTreeManager = class {
28847
28908
  * The shim pins the same Node.js runtime OpenTag uses, so a Session cannot resolve a different
28848
28909
  * one from the user's shell configuration.
28849
28910
  */
28911
+ #prepareShim() {
28912
+ if (this.#shimPreparation && Date.now() < this.#shimRetryAt) return this.#shimPreparation;
28913
+ if (this.#shimPreparation && this.#shimRetryAt === 0) return this.#shimPreparation;
28914
+ this.#shimRetryAt = 0;
28915
+ this.#shimPreparation = this.#writeShim().catch((error) => {
28916
+ this.#logger.warn({ err: describe(error) }, "Context Tree shim could not be created");
28917
+ return false;
28918
+ }).then((success) => {
28919
+ if (!success) this.#shimRetryAt = Date.now() + this.#failureCooldownMs;
28920
+ return success;
28921
+ });
28922
+ return this.#shimPreparation;
28923
+ }
28850
28924
  async #writeShim() {
28851
28925
  if (!this.#package) return false;
28852
28926
  if (this.#platform === "win32") {
@@ -28869,10 +28943,10 @@ var ContextTreeManager = class {
28869
28943
  reason
28870
28944
  };
28871
28945
  }
28872
- #joinPreparation(cwd, config, target) {
28946
+ #joinPreparation(cwd, config, target, executableFailure) {
28873
28947
  const current = this.#inFlight.get(cwd);
28874
28948
  if (current?.target === target) return current.promise;
28875
- const prepared = this.#serialize(() => this.#ensureAgentOnce(cwd, config)).catch((error) => {
28949
+ const prepared = this.#serialize(async () => executableFailure ? this.#unavailable(executableFailure, config) : this.#ensureAgentOnce(cwd, config)).catch((error) => {
28876
28950
  this.#logger.error({ err: describe(error) }, "Context Tree preparation raised an unexpected failure");
28877
28951
  return {
28878
28952
  status: "unavailable",
@@ -31313,7 +31387,6 @@ function isStorageOrPermissionError(error) {
31313
31387
  const code = error?.code;
31314
31388
  return code === "EACCES" || code === "EPERM" || code === "EROFS";
31315
31389
  }
31316
- const UNREPAIRABLE = /* @__PURE__ */ new Set(["unsupported_platform", "global_bin_unavailable"]);
31317
31390
  const GRANT_REPLAY_RETENTION_MS = 6e4;
31318
31391
  const logger$3 = createLogger("runtime-provider-cli-reconciler");
31319
31392
  /**
@@ -31360,10 +31433,11 @@ var ProviderCliReconciler = class {
31360
31433
  this.#closePromise ??= this.#performClose();
31361
31434
  return this.#closePromise;
31362
31435
  }
31363
- /** Re-inspect each published CLI so heartbeat freshness reflects current local state. */
31436
+ /** Re-inspect published CLIs and active requirement Providers without mutating repair state. */
31364
31437
  async refreshPublishedImCliReadiness() {
31365
31438
  if (this.#closed || this.#signal?.aborted) return;
31366
- await Promise.all([...this.#imCliPublished.keys()].map((provider) => this.#refreshImCli(provider)));
31439
+ const providers = /* @__PURE__ */ new Set([...this.#imCliPublished.keys(), ...[...this.#current.values()].map((requirement) => requirement.provider)]);
31440
+ await Promise.all([...providers].map((provider) => this.#refreshPassive(provider)));
31367
31441
  }
31368
31442
  async #performClose() {
31369
31443
  this.#closed = true;
@@ -31385,12 +31459,11 @@ var ProviderCliReconciler = class {
31385
31459
  * Return the exact selection already accepted by daemon readiness.
31386
31460
  *
31387
31461
  * An already-accepted selection that still matches the live CLI is admitted
31388
- * immediately. A never-accepted, already-healthy live CLI joins the in-flight
31389
- * provider reconcile and is admitted only when path, fingerprint, version, and
31390
- * generation stay identical across the wait. A provider that was accepted once
31391
- * and later failed or rotated is not treated as a fresh cold start. Caller
31392
- * cancellation interrupts inspect, reconcile wait, and publication wait without
31393
- * cancelling shared work or another waiter.
31462
+ * immediately. A never-accepted, already-healthy live CLI joins in-flight owner
31463
+ * work when present, otherwise confirms read-only if identity stays stable. A
31464
+ * provider that was accepted once and later failed or rotated is not treated as
31465
+ * a fresh cold start and does not start Run-triggered ensure. Caller cancellation
31466
+ * interrupts inspect and shared-work wait without cancelling other waiters.
31394
31467
  */
31395
31468
  async readySelectionForRun(provider, signal) {
31396
31469
  try {
@@ -31409,14 +31482,26 @@ var ProviderCliReconciler = class {
31409
31482
  if (this.#closed) return void 0;
31410
31483
  const accepted = this.#readySelection.get(provider);
31411
31484
  if (accepted && live && selectionsMatch(accepted, live)) return { ...live };
31412
- const initialConfirmation = accepted === void 0 && live !== void 0 && !this.#acceptedOnce.has(provider);
31413
- await this.#awaitReadiness(this.#publishCurrentProvider(provider, "checking"), signal);
31414
- const status = await this.#awaitReadiness(this.#reconcileProvider(provider), signal);
31485
+ if (!(accepted === void 0 && live !== void 0 && !this.#acceptedOnce.has(provider)) || !live) return void 0;
31486
+ const existing = this.#providerRepairJob(provider);
31487
+ if (existing) {
31488
+ const outcome = await this.#awaitReadiness(existing, signal);
31489
+ this.#throwIfReadinessAborted(signal);
31490
+ if (this.#closed || outcome.status !== "ready") return void 0;
31491
+ return this.#admitUnchangedInitialSelection(provider, live, signal);
31492
+ }
31493
+ return this.#confirmInitialSelection(provider, live, signal);
31494
+ }
31495
+ async #confirmInitialSelection(provider, live, signal) {
31496
+ const liveAfter = await this.#awaitReadiness(this.#inspectLiveSelection(provider), signal);
31415
31497
  this.#throwIfReadinessAborted(signal);
31416
31498
  if (this.#closed) return void 0;
31417
- await this.#awaitReadiness(this.#publishCurrentProvider(provider, status), signal);
31418
- if (!initialConfirmation || !live) return void 0;
31419
- return this.#admitUnchangedInitialSelection(provider, live, signal);
31499
+ if (!liveAfter || !selectionsMatch(live, liveAfter)) return void 0;
31500
+ const accepted = this.#readySelection.get(provider);
31501
+ if (this.#acceptedOnce.has(provider)) return accepted && selectionsMatch(accepted, liveAfter) ? { ...liveAfter } : void 0;
31502
+ this.#readySelection.set(provider, liveAfter);
31503
+ this.#acceptedOnce.add(provider);
31504
+ return { ...liveAfter };
31420
31505
  }
31421
31506
  async #admitUnchangedInitialSelection(provider, live, signal) {
31422
31507
  const acceptedAfter = this.#readySelection.get(provider);
@@ -31503,22 +31588,77 @@ var ProviderCliReconciler = class {
31503
31588
  });
31504
31589
  }
31505
31590
  async #prewarmProvider(provider, mode) {
31506
- if (mode === "inspect") return this.#refreshImCli(provider);
31591
+ if (mode === "inspect") {
31592
+ this.#publishImCli(provider, "checking");
31593
+ const status = (await this.#inspectProvider(provider))?.readiness ?? "unavailable";
31594
+ this.#publishImCli(provider, status);
31595
+ return status;
31596
+ }
31507
31597
  this.#publishImCli(provider, "checking");
31508
- const status = await this.#reconcileProvider(provider, "auto");
31509
- this.#publishImCli(provider, status);
31510
- return status;
31598
+ const outcome = await this.#reconcileProvider(provider, "auto");
31599
+ this.#publishImCli(provider, outcome.status);
31600
+ return outcome.status;
31601
+ }
31602
+ async #refreshPassive(provider) {
31603
+ const published = this.#imCliPublished.has(provider);
31604
+ const targets = [...this.#current.values()].filter((requirement) => requirement.provider === provider).map((requirement) => ({
31605
+ integrationId: requirement.integrationId,
31606
+ lastStatus: requirement.lastStatus,
31607
+ requestId: requirement.requestId
31608
+ }));
31609
+ const repairAtStart = this.#providerRepairJob(provider);
31610
+ const selectionAtStart = this.#readySelection.get(provider);
31611
+ const inspection = await this.#inspectProvider(provider);
31612
+ if (this.#closed || this.#signal?.aborted) return;
31613
+ const readiness = inspection?.readiness ?? "unavailable";
31614
+ if (published && this.#imCliPublished.has(provider)) this.#publishImCli(provider, readiness);
31615
+ if (this.#passiveOverlappedRepair(provider, targets, repairAtStart, selectionAtStart)) return;
31616
+ await this.#publishPassiveInspection(provider, inspection, targets, repairAtStart, selectionAtStart);
31617
+ }
31618
+ async #publishPassiveInspection(provider, inspection, targets, repairAtStart, selectionAtStart) {
31619
+ const liveTargets = targets.flatMap((target) => {
31620
+ const current = this.#current.get(target.integrationId);
31621
+ return current?.requestId === target.requestId ? [current] : [];
31622
+ });
31623
+ if (liveTargets.length === 0) return;
31624
+ if (inspection?.readiness === "ready") {
31625
+ const ready = await readySelectionFromInspect(this.#manager.layout, provider, inspection);
31626
+ if (this.#closed || this.#signal?.aborted) return;
31627
+ if (this.#passiveOverlappedRepair(provider, targets, repairAtStart, selectionAtStart)) return;
31628
+ const stillLive = liveTargets.filter((target) => this.#current.get(target.integrationId)?.requestId === target.requestId);
31629
+ if (ready) {
31630
+ this.#readySelection.set(provider, ready);
31631
+ this.#acceptedOnce.add(provider);
31632
+ } else this.#readySelection.delete(provider);
31633
+ await Promise.all(stillLive.map((requirement) => this.#publishArtifact(requirement, ready ? "ready" : "unavailable")));
31634
+ return;
31635
+ }
31636
+ const code = inspection?.diagnostic?.code;
31637
+ const publicReason = publicProviderCliArtifactReason(code);
31638
+ const inspectReason = providerCliArtifactFailureIsManual({
31639
+ reason: code,
31640
+ stage: "inspect"
31641
+ }) ? publicReason : void 0;
31642
+ await Promise.all(liveTargets.map((requirement) => {
31643
+ if (this.#current.get(requirement.integrationId)?.requestId !== requirement.requestId) return Promise.resolve();
31644
+ return this.#publishArtifact(requirement, "unavailable", requirement.lastStatus === "unavailable" ? requirement.lastReason ?? inspectReason : inspectReason);
31645
+ }));
31511
31646
  }
31512
- async #refreshImCli(provider) {
31513
- this.#publishImCli(provider, "checking");
31514
- const status = await this.#inspectImCli(provider);
31515
- this.#publishImCli(provider, status);
31516
- return status;
31647
+ #providerRepairJob(provider) {
31648
+ return this.#providerJobs.get(`${provider}:managed-only`) ?? this.#providerJobs.get(`${provider}:auto`);
31649
+ }
31650
+ #passiveOverlappedRepair(provider, targets, repairAtStart, selectionAtStart) {
31651
+ if (repairAtStart || this.#providerRepairJob(provider)) return true;
31652
+ if (this.#readySelection.get(provider) !== selectionAtStart) return true;
31653
+ return targets.some((target) => {
31654
+ const current = this.#current.get(target.integrationId);
31655
+ return current?.requestId === target.requestId && current.lastStatus !== target.lastStatus;
31656
+ });
31517
31657
  }
31518
- #inspectImCli(provider) {
31658
+ #inspectProvider(provider) {
31519
31659
  const existing = this.#inspectionJobs.get(provider);
31520
31660
  if (existing) return existing;
31521
- const job = this.#manager.inspect(provider).then((inspection) => inspection.readiness).catch(() => "unavailable").finally(() => {
31661
+ const job = this.#manager.inspect(provider).catch(() => void 0).finally(() => {
31522
31662
  if (this.#inspectionJobs.get(provider) === job) this.#inspectionJobs.delete(provider);
31523
31663
  });
31524
31664
  this.#inspectionJobs.set(provider, job);
@@ -31555,8 +31695,10 @@ var ProviderCliReconciler = class {
31555
31695
  };
31556
31696
  this.#current.set(frame.integrationId, tracked);
31557
31697
  await this.#publishArtifact(tracked, "checking");
31558
- const status = await this.#reconcileProvider(frame.provider);
31559
- await this.#publishCurrentProvider(frame.provider, status);
31698
+ const outcome = await this.#reconcileProvider(frame.provider);
31699
+ const current = this.#current.get(frame.integrationId);
31700
+ if (!current || current.requestId !== tracked.requestId) return;
31701
+ await this.#publishArtifact(current, outcome.status, outcome.reason);
31560
31702
  }
31561
31703
  async #reconcileProvider(provider, mode = "managed-only") {
31562
31704
  const key = `${provider}:${mode}`;
@@ -31572,22 +31714,32 @@ var ProviderCliReconciler = class {
31572
31714
  try {
31573
31715
  let inspection = await this.#manager.inspect(provider);
31574
31716
  if (inspection.readiness !== "ready") {
31575
- if (inspection.diagnostic && UNREPAIRABLE.has(inspection.diagnostic.code)) return "unavailable";
31576
- await this.#ensureConverging(provider, mode);
31717
+ if (providerCliArtifactFailureIsManual({
31718
+ reason: inspection.diagnostic?.code,
31719
+ stage: "inspect"
31720
+ })) {
31721
+ this.#readySelection.delete(provider);
31722
+ return unavailableOutcome(inspection.diagnostic?.code, "inspect");
31723
+ }
31724
+ const ensured = await this.#ensureConverging(provider, mode);
31577
31725
  inspection = await this.#manager.inspect(provider);
31726
+ if (inspection.readiness !== "ready") {
31727
+ this.#readySelection.delete(provider);
31728
+ return unavailableOutcome(ensured.diagnostic?.code ?? inspection.diagnostic?.code, "ensure");
31729
+ }
31578
31730
  }
31579
31731
  if (inspection.readiness === "ready") {
31580
31732
  const ready = await readySelectionFromInspect(this.#manager.layout, provider, inspection);
31581
31733
  if (!ready) {
31582
31734
  this.#readySelection.delete(provider);
31583
- return "unavailable";
31735
+ return { status: "unavailable" };
31584
31736
  }
31585
31737
  this.#readySelection.set(provider, ready);
31586
31738
  this.#acceptedOnce.add(provider);
31587
- return "ready";
31739
+ return { status: "ready" };
31588
31740
  }
31589
31741
  this.#readySelection.delete(provider);
31590
- return "unavailable";
31742
+ return unavailableOutcome(inspection.diagnostic?.code, "ensure");
31591
31743
  } catch (error) {
31592
31744
  logger$3.debug({
31593
31745
  code: "provider_reconcile_failed",
@@ -31595,7 +31747,7 @@ var ProviderCliReconciler = class {
31595
31747
  error: String(error)
31596
31748
  }, "Provider CLI reconciliation failed");
31597
31749
  this.#readySelection.delete(provider);
31598
- return "unavailable";
31750
+ return { status: "unavailable" };
31599
31751
  }
31600
31752
  }
31601
31753
  /**
@@ -31635,10 +31787,6 @@ var ProviderCliReconciler = class {
31635
31787
  wait.then(() => finish(), finish);
31636
31788
  });
31637
31789
  }
31638
- async #publishCurrentProvider(provider, status) {
31639
- const current = [...this.#current.values()].filter((requirement) => requirement.provider === provider);
31640
- await Promise.all(current.map((requirement) => this.#publishArtifact(requirement, status)));
31641
- }
31642
31790
  async #handleGrant(frame) {
31643
31791
  this.#pruneGrants();
31644
31792
  if (!this.#acceptsGrant(frame)) return;
@@ -31740,9 +31888,12 @@ var ProviderCliReconciler = class {
31740
31888
  this.#grants.clear();
31741
31889
  this.#current.clear();
31742
31890
  }
31743
- async #publishArtifact(frame, status) {
31891
+ async #publishArtifact(frame, status, reason) {
31744
31892
  const current = this.#current.get(frame.integrationId);
31745
31893
  if (!current || current.requestId !== frame.requestId) return;
31894
+ const publicReason = status === "unavailable" ? reason : void 0;
31895
+ if (current.lastPublishOk !== false && current.lastStatus === status && current.lastReason === publicReason) return;
31896
+ const includeReason = publicReason !== void 0 && this.#connection.capabilityVersion(RUNTIME_CAPABILITY.providerCliReconcile) === 2;
31746
31897
  const payload = {
31747
31898
  type: "provider-cli:artifact:status",
31748
31899
  requestId: frame.requestId,
@@ -31750,9 +31901,17 @@ var ProviderCliReconciler = class {
31750
31901
  agentId: frame.agentId,
31751
31902
  integrationId: frame.integrationId,
31752
31903
  credentialGeneration: frame.credentialGeneration,
31753
- status
31904
+ status,
31905
+ ...includeReason ? { reason: publicReason } : {}
31754
31906
  };
31755
- await this.#connection.send(payload, { priority: "result" });
31907
+ try {
31908
+ await this.#connection.send(payload, { priority: "result" });
31909
+ current.lastStatus = status;
31910
+ current.lastReason = publicReason;
31911
+ current.lastPublishOk = true;
31912
+ } catch {
31913
+ current.lastPublishOk = false;
31914
+ }
31756
31915
  }
31757
31916
  async #publishValidation(frame, result) {
31758
31917
  const payload = {
@@ -31810,6 +31969,18 @@ async function readySelectionFromInspect(layout, provider, inspection) {
31810
31969
  function selectionsMatch(left, right) {
31811
31970
  return left.fingerprint === right.fingerprint && left.generation === right.generation && left.path === right.path && left.version === right.version;
31812
31971
  }
31972
+ function unavailableOutcome(code, stage) {
31973
+ const publicReason = publicProviderCliArtifactReason(code);
31974
+ if (!publicReason) return { status: "unavailable" };
31975
+ if (stage === "inspect" && !providerCliArtifactFailureIsManual({
31976
+ reason: code,
31977
+ stage
31978
+ })) return { status: "unavailable" };
31979
+ return {
31980
+ status: "unavailable",
31981
+ reason: publicReason
31982
+ };
31983
+ }
31813
31984
  /**
31814
31985
  * Wait for shared work without cancelling it. One waiter's abort only stops that
31815
31986
  * waiter; the underlying job keeps running for everyone else.
@@ -36338,7 +36509,6 @@ async function createClientRuntime(connection, options) {
36338
36509
  providers,
36339
36510
  providerEnvironmentPath: (sessionId) => credentialEnvironment.pathForSession(sessionId),
36340
36511
  providerCliLaunchPath: (sessionId) => providerCliTurnPlans.sessionDir(sessionId),
36341
- inheritedPath: sourceEnvironment.PATH,
36342
36512
  slackConfigWritableRoot: (sessionId) => credentialEnvironment.activeSlackConfigDirForSession(sessionId),
36343
36513
  proofManager,
36344
36514
  workspace
@@ -37410,7 +37580,7 @@ function redactSecrets(value) {
37410
37580
  //#endregion
37411
37581
  //#region package.json
37412
37582
  var name = "open-tag";
37413
- var version = "0.0.3";
37583
+ var version = "0.0.4";
37414
37584
  //#endregion
37415
37585
  //#region src/build-info.ts
37416
37586
  const CHANNEL = "prod";
@@ -40734,6 +40904,7 @@ function formatImBindingDiagnostics(value) {
40734
40904
  `slackIdentityClosure\t${value.slackIdentityClosure ? `${value.slackIdentityClosure.status}${value.slackIdentityClosure.verifiedAt ? ` (${value.slackIdentityClosure.verifiedAt})` : ""}` : "-"}`,
40735
40905
  `agentRuntimeReadiness\t${value.agentRuntimeReadiness}`,
40736
40906
  `providerCliReadiness\t${value.providerCliReadiness}`,
40907
+ `providerCliReason\t${value.providerCliReason ?? "-"}`,
40737
40908
  `credentialExecutionReadiness\t${value.credentialExecutionReadiness}`,
40738
40909
  `credentialExecutionReason\t${value.credentialExecutionReason ?? "-"}`,
40739
40910
  `connection\t${value.connection ? `${value.connection.state} (observed ${value.connection.observedAt})` : "not applicable"}`,
@@ -42270,15 +42441,12 @@ function providerCliRepairCommand(provider) {
42270
42441
  const flag = provider === "all" ? "all" : providerCliLabel(provider);
42271
42442
  return `"$HOME/.local/bin/${channelConfig.binName}" provider-cli ensure --provider ${flag}`;
42272
42443
  }
42273
- const PROVIDER_CLI_MANUAL_REASONS = /* @__PURE__ */ new Set([
42274
- "global_bin_unavailable",
42275
- "integrity_failed",
42276
- "unsupported_platform",
42277
- "version_incompatible"
42278
- ]);
42279
42444
  /** False when repeating ensure cannot change the local fact without a user or release change. */
42280
- function providerCliCanAutoRepair(reason) {
42281
- return !PROVIDER_CLI_MANUAL_REASONS.has(reason);
42445
+ function providerCliCanAutoRepair(reason, stage = "ensure") {
42446
+ return !providerCliArtifactFailureIsManual({
42447
+ reason,
42448
+ stage
42449
+ });
42282
42450
  }
42283
42451
  /**
42284
42452
  * Failures that describe another OpenTag process holding the provider lock: transient, safe to
@@ -43390,7 +43558,7 @@ function resolveContextTreeAssets(deps = {}) {
43390
43558
  return deps.contextTreePackage ?? resolveContextTreePackage();
43391
43559
  }
43392
43560
  function contextTreeConfigPath(home) {
43393
- return join(home, "config", "context-tree.json");
43561
+ return join(home, "config", "context-tree", "config.json");
43394
43562
  }
43395
43563
  async function readContextTreeConfig(home) {
43396
43564
  let content;
@@ -45329,7 +45497,7 @@ function renderInspectionLines(inspection, nextAction) {
45329
45497
  function nextActionFor(inspection) {
45330
45498
  if (inspection.state === "ready") return void 0;
45331
45499
  const reason = inspection.diagnostic?.code ?? (inspection.readiness === "install" ? "not_installed" : "unavailable");
45332
- if (!providerCliCanAutoRepair(reason)) return void 0;
45500
+ if (!providerCliCanAutoRepair(reason, "inspect")) return void 0;
45333
45501
  return {
45334
45502
  provider: inspection.provider,
45335
45503
  command: providerCliRepairCommand(inspection.provider),
@@ -45910,4 +46078,4 @@ function createProgram(options = {}) {
45910
46078
  //#endregion
45911
46079
  export { CLI_PACKAGE_NAME as A, resolveOpenTagHomeLayout as B, channelConfig as C, formatAgentList as D, formatAgentCreated as E, executeCommand as F, presentCommand as I, toCommandError as L, CommandError as M, EXIT_CODES as N, CommanderError as O, commandExitCode as P, configureClientLoggerForService as R, resolveChannelEnvironment as S, formatAgentBound as T, runAgentCreate as _, runSessionList as a, selectComputer as b, runLogin as c, runProviderCliEnsure as d, formatComputerList as f, runAgentBind as g, runAgentShow as h, runSessionCreate as i, CLI_VERSION as j, CHANNEL as k, runDoctor as l, runAgentList as m, formatSessionCommandResult as n, runSessionSend as o, buildChildEnvironment as p, formatSessionList as r, runProviderCliInspect as s, createProgram as t, listComputers as u, runAgentDelete as v, formatAgent as w, resolveCommandContext as x, runAgentUpdate as y, resolveOpenTagHome as z };
45912
46080
 
45913
- //# sourceMappingURL=program-CY6rrPEk.mjs.map
46081
+ //# sourceMappingURL=program-mNRMp_S4.mjs.map