@aiwg/cli 2026.10.6 → 2026.10.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/bin/aiwg.mjs +4 -2
  2. package/dist/src/a2a/agent-card.js +11 -0
  3. package/dist/src/api/index.d.ts +1 -0
  4. package/dist/src/api/index.js +1 -0
  5. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/contract.json +103 -0
  6. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-config.schema.json +212 -0
  7. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-set.schema.json +240 -0
  8. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-manifest.schema.json +451 -0
  9. package/dist/src/artifacts/fortemi-shard-export.js +284 -0
  10. package/dist/src/artifacts/index-preparation.js +2 -0
  11. package/dist/src/auth/client.js +5 -1
  12. package/dist/src/auth/credential-store.js +5 -1
  13. package/dist/src/auth/sensitive-input.js +151 -0
  14. package/dist/src/channel/manager.mjs +3 -0
  15. package/dist/src/cli/handlers/agentcard.js +47 -25
  16. package/dist/src/cli/handlers/auth.js +22 -2
  17. package/dist/src/cli/handlers/decision.js +3 -2
  18. package/dist/src/cli/handlers/effect.js +4 -84
  19. package/dist/src/cli/handlers/guided-setup.js +1 -1
  20. package/dist/src/cli/handlers/init.js +1 -1
  21. package/dist/src/cli/handlers/install.js +6 -0
  22. package/dist/src/cli/handlers/marketplace.js +15 -4
  23. package/dist/src/cli/handlers/setup.js +24 -20
  24. package/dist/src/cli/handlers/use.js +57 -15
  25. package/dist/src/cli/prompt-utils.js +73 -0
  26. package/dist/src/config/gitignore.js +4 -0
  27. package/dist/src/config/workspace.js +2 -1
  28. package/dist/src/decision/adapters/jev.js +20 -31
  29. package/dist/src/decision/ask.js +6 -1
  30. package/dist/src/decision/benchmark.js +19 -6
  31. package/dist/src/decision/qualification/gate-evidence.js +6 -4
  32. package/dist/src/decision/qualification/load-staged.js +47 -10
  33. package/dist/src/decision/qualification/runner.js +7 -3
  34. package/dist/src/discovery/interview.js +10 -5
  35. package/dist/src/discovery/plan-apply.js +26 -30
  36. package/dist/src/discovery/plan-validation.js +2 -2
  37. package/dist/src/effects/host.js +91 -0
  38. package/dist/src/extensions/commands/definitions.js +2 -2
  39. package/dist/src/extensions/project-local-gitignore.js +2 -0
  40. package/dist/src/issues/cli.js +16 -5
  41. package/dist/src/issues/live.js +44 -12
  42. package/dist/src/marketplace/exchange.js +18 -6
  43. package/dist/src/marketplace/provenance.js +3 -2
  44. package/dist/src/mcp/adapters/codex-runtime.js +46 -76
  45. package/dist/src/mcp/cli.mjs +17 -90
  46. package/dist/src/mcp/grok-build-config.mjs +2 -1
  47. package/dist/src/mcp/muse-native-config.mjs +2 -1
  48. package/dist/src/mcp/tools/worker-invoke.mjs +13 -2
  49. package/dist/src/memory/project-registry.js +2 -1
  50. package/dist/src/mission-protocol/worker-status.js +91 -1
  51. package/dist/src/models/worker-installation.d.mts +1 -1
  52. package/dist/src/models/worker-installation.mjs +65 -4
  53. package/dist/src/packages/adapters/git.js +99 -23
  54. package/dist/src/packages/adapters/gitea.js +8 -27
  55. package/dist/src/packages/adapters/github.js +7 -25
  56. package/dist/src/packages/registry.js +4 -4
  57. package/dist/src/process/cli-command.d.mts +5 -0
  58. package/dist/src/process/cli-command.mjs +42 -0
  59. package/dist/src/serve/worker-invocation.js +10 -0
  60. package/dist/src/smiths/context-pipeline/claude-hook.js +9 -9
  61. package/dist/src/smiths/context-pipeline/context-file-safety.js +330 -9
  62. package/dist/src/smiths/context-pipeline/finalization.js +3 -2
  63. package/dist/src/smiths/context-pipeline/generator.js +7 -19
  64. package/dist/src/smiths/context-pipeline/legacy-inject.js +3 -10
  65. package/dist/src/smiths/context-pipeline/managed-hook.js +5 -5
  66. package/dist/src/smiths/context-pipeline/workspace-context.js +4 -12
  67. package/dist/src/tracker/project-inputs.js +2 -0
  68. package/dist/src/update/notifier.mjs +2 -4
  69. package/package.json +1 -1
  70. package/tools/agents/providers/base.mjs +5 -1
  71. package/tools/agents/providers/claude.mjs +5 -2
  72. package/tools/agents/providers/codex.mjs +4 -1
  73. package/tools/agents/providers/context-write.mjs +28 -0
  74. package/tools/agents/providers/copilot.mjs +5 -2
  75. package/tools/agents/providers/hermes.mjs +5 -2
  76. package/tools/agents/providers/omp.mjs +3 -1
  77. package/tools/agents/providers/rule-delivery.mjs +29 -12
  78. package/tools/agents/providers/warp.mjs +4 -1
  79. package/tools/agents/providers/windsurf.mjs +6 -3
  80. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +26 -0
  81. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +7 -1
  82. package/tools/ralph-external/lib/recovery-probe.mjs +14 -4
  83. package/tools/ralph-external/process-monitor.mjs +42 -4
  84. package/tools/ralph-external/session-launcher.mjs +125 -3
@@ -487,6 +487,10 @@ function workerLaunchRefusal(outcome) {
487
487
  : 'Worker launch did not return an attached child');
488
488
  }
489
489
 
490
+ // Only the admitted direct owner can supply inputs for an automatic correction.
491
+ // Neither worker output nor a caller-supplied result can populate this context.
492
+ const directStartupCorrectionContext = new WeakMap();
493
+
490
494
  async function launchOneWorker(owner, options) {
491
495
  const { inspectWorkerPath: inspect, revalidateWorkerInvocationPlan: validate } = await importImpl(import.meta.url, 'serve/worker-invocation.js');
492
496
  const { qualificationDigest } = await importImpl(import.meta.url, 'models/qualification-digest.mjs');
@@ -664,6 +668,7 @@ async function launchOneWorker(owner, options) {
664
668
  // worker-writable captureRoot fell back to the in-process ledger) is
665
669
  // folded in up front so every exit path reports it.
666
670
  const ledgerDiagnostics = admission.ledgerDiagnostic ? [admission.ledgerDiagnostic] : [];
671
+ const startupQualification = structuredClone(inputs.drivers[invocation.selected.cli].qualification);
667
672
  const settleLedger = async (outcome) => {
668
673
  const diagnostic = await settleDirectWorker(admission, outcome);
669
674
  if (diagnostic) ledgerDiagnostics.push(diagnostic);
@@ -747,11 +752,22 @@ async function launchOneWorker(owner, options) {
747
752
  // writes; `.catch(() => {})` keeps a prior failure from poisoning it, and
748
753
  // the per-call promise returned to the caller still reflects this write's
749
754
  // own outcome.
755
+ let latestProcessRecord = null;
750
756
  const publish = (name, value) => {
757
+ if (name === 'process.json') {
758
+ value = { ...value, recovery: { ...admission.breaker.recoveryStatus(), observedAt: new Date().toISOString() } };
759
+ if (options.startupCorrectionRecord) value.startupCorrection = options.startupCorrectionRecord;
760
+ latestProcessRecord = value;
761
+ }
751
762
  const write = publishing.catch(() => {}).then(() => writeRecord(runDir, name, value, inspect));
752
763
  publishing = write.catch(error => { publishFailures.push({ name, error: error?.message ?? String(error) }); });
753
764
  return write;
754
765
  };
766
+ const publishRecovery = () => {
767
+ if (latestProcessRecord) return publish('process.json', latestProcessRecord);
768
+ };
769
+ const onScopeTransition = () => { publishRecovery()?.catch(() => {}); };
770
+ admission.breaker.on('transition', onScopeTransition);
755
771
  const handles = [];
756
772
  let outcome;
757
773
  let artifacts;
@@ -914,6 +930,8 @@ async function launchOneWorker(owner, options) {
914
930
  // accumulated across this attempt's lifecycle on the returned result,
915
931
  // mirroring `publishFailures` — never silently discarded.
916
932
  if (ledgerDiagnostics.length && result) result.ledgerDiagnostics = ledgerDiagnostics.slice();
933
+ directStartupCorrectionContext.set(owner, { saved, invocation, loadInputs, clock, dispatchGuard,
934
+ ledger: admission.ledger, inspect, qualification: startupQualification });
917
935
  return result;
918
936
  } catch (error) {
919
937
  if (!outcome?.child) {
@@ -985,6 +1003,8 @@ async function launchOneWorker(owner, options) {
985
1003
  if (ledgerDiagnostics.length) error.ledgerDiagnostics = ledgerDiagnostics;
986
1004
  throw error;
987
1005
  } finally {
1006
+ admission.breaker.removeListener('transition', onScopeTransition);
1007
+ await publishing;
988
1008
  try { await artifacts?.cleanup(); } finally {
989
1009
  // Never chmod a worker-replaced pathname; only the inodes we opened.
990
1010
  for (const handle of handles) { await handle.chmod(0o600).catch(() => {}); await handle.close().catch(() => {}); }
@@ -3916,6 +3936,102 @@ export function classifyAttemptForSettlement(invocation, result, { quotaAccount
3916
3936
  inferenceProvider: selected.inferenceProvider ?? null, quotaAccount, installationQualifier };
3917
3937
  }
3918
3938
 
3939
+ /** Refresh only a demonstrated stale executable binding. Installation, login,
3940
+ * permissions, models, working directories and policy are never repaired here.
3941
+ * One retry shares the original task clock and the existing atomic ledgers. */
3942
+ async function correctDirectWorkerStartup(owner, options, previous) {
3943
+ const context = directStartupCorrectionContext.get(owner);
3944
+ if (!context || options.correctStartup !== undefined || previous?.errorCode !== 'ENOENT'
3945
+ || previous.termination !== 'confirmed' || previous.exitCode != null || previous.signal != null
3946
+ || previous.cancelled || previous.timedOut || previous.publishFailures?.length
3947
+ || classifyLaunchOutcomeForRecovery(previous).reasonCode !== 'NOT_FOUND') return previous;
3948
+ const { saved, invocation, loadInputs, clock, dispatchGuard, ledger, inspect, qualification } = context;
3949
+ const record = { action: 'refresh-executable-binding', reasonCode: 'NOT_FOUND', state: 'stopped',
3950
+ previousInvocationId: previous.invocationId };
3951
+ const recordAt = async (runDir, invocationId) => {
3952
+ const processRecord = JSON.parse((await boundedPrivateRead(join(runDir, 'process.json'), 65536, inspect)).toString('utf8'));
3953
+ if (processRecord.origin !== 'launcher' || processRecord.invocationId !== invocationId) throw new Error('Correction record identity changed');
3954
+ await (options.publishRecord ?? publishWorkerRecord)(runDir, 'process.json', { ...processRecord, startupCorrection: { ...record } }, inspect);
3955
+ };
3956
+ const stop = async code => {
3957
+ record.stopCode = code;
3958
+ previous.startupCorrection = { ...record };
3959
+ await recordAt(options.runDir, previous.invocationId);
3960
+ return previous;
3961
+ };
3962
+ const guardStop = () => dispatchGuard?.isCancelled?.() === true ? 'CANCELLED'
3963
+ : dispatchGuard?.deadlineAtMs != null && (dispatchGuard.nowMs?.() ?? Date.now()) >= dispatchGuard.deadlineAtMs ? 'DEADLINE' : null;
3964
+ if (ledger?.n >= ledger?.maxAttempts) return stop('ATTEMPT_LIMIT');
3965
+ if (guardStop()) return stop(guardStop());
3966
+ let inputs;
3967
+ let plan;
3968
+ const retryId = randomUUID();
3969
+ const finalPath = `${invocation.finalPath}.recovery-${retryId}`;
3970
+ try {
3971
+ inputs = await loadInputs();
3972
+ const { resolveWorkerInvocation } = await importImpl(import.meta.url, 'serve/worker-invocation.js');
3973
+ plan = await resolveWorkerInvocation(inputs.snapshot, { ...saved.request, final_output: finalPath }, inputs.drivers, clock());
3974
+ } catch { return stop('QUALIFICATION_REFUSED'); }
3975
+ if (guardStop()) return stop(guardStop());
3976
+ if (plan?.status !== 'eligible' || plan.mode !== 'direct' || plan.invocation?.readiness !== 'launchable') return stop('QUALIFICATION_REFUSED');
3977
+ if (plan.invocation.argv[0] === invocation.argv[0]) return stop('UNCHANGED_EXECUTABLE');
3978
+ // A freshly requalified installation may have new binary/proof digests.
3979
+ // Preserve every effective adapter declaration and installation boundary.
3980
+ const effectiveQualification = value => {
3981
+ const copy = structuredClone(value);
3982
+ const stripEvidence = evidence => {
3983
+ if (evidence) for (const key of ['scopeDigest', 'fixtureDigest', 'verifiedAt', 'evidenceRef']) delete evidence[key];
3984
+ };
3985
+ stripEvidence(copy.adapter?.qualification);
3986
+ for (const profile of Object.values(copy.adapter?.invocationProfiles ?? {})) stripEvidence(profile.installationQualification);
3987
+ if (copy.installation) {
3988
+ delete copy.installation.executable;
3989
+ delete copy.installation.evidence;
3990
+ }
3991
+ if (copy.stdinFramerBinding) delete copy.stdinFramerBinding.storeDigest;
3992
+ return JSON.stringify(copy);
3993
+ };
3994
+ if (effectiveQualification(qualification) !== effectiveQualification(inputs.drivers[invocation.selected.cli]?.qualification)) return stop('BINDING_CHANGED');
3995
+ // The resolver is authoritative for the replacement executable. Everything
3996
+ // else must remain identical, including qualified selection, policy, limits,
3997
+ // framing, environment, controls, prompt digest and workspace. Only fresh
3998
+ // output names and expiration are expected to differ between attempts.
3999
+ const binding = value => {
4000
+ const copy = structuredClone(value);
4001
+ const replacements = ['stderrPath', 'stdoutPath', 'finalPath'].map(key => [copy[key], `<${key}>`]);
4002
+ const normalize = value => replacements.find(([path]) => path === value)?.[1] ?? value;
4003
+ copy.argv = copy.argv.map((value, index) => index === 0 ? '<executable>' : normalize(value));
4004
+ delete copy.selected.qualificationDigest;
4005
+ for (const key of ['stdoutPath', 'stderrPath', 'finalPath', 'expiresAt', 'shellCommand', 'profileDigest']) delete copy[key];
4006
+ return JSON.stringify(copy);
4007
+ };
4008
+ if (binding(plan.invocation) !== binding(invocation)) return stop('BINDING_CHANGED');
4009
+ const invocationPath = join(options.runDir, `recovery-plan-${retryId}.json`);
4010
+ const runDir = `${options.runDir}.recovery-${retryId}`;
4011
+ if (guardStop()) return stop(guardStop());
4012
+ let retry;
4013
+ try {
4014
+ const planHandle = await withWorkerPath(invocationPath, target => open(target,
4015
+ constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600));
4016
+ try { await planHandle.writeFile(JSON.stringify(plan)); await planHandle.sync(); } finally { await planHandle.close(); }
4017
+ // Publication happens before any retry launch. A record failure stops work.
4018
+ record.state = 'applied';
4019
+ await recordAt(options.runDir, previous.invocationId);
4020
+ retry = await launchOneWorker(owner, { ...options, invocationPath, runDir, dispatchGuard,
4021
+ inputs: undefined, loadInputs, retryOf: { invocationId: previous.invocationId, runDir: options.runDir },
4022
+ correctStartup: { reasonCode: 'NOT_FOUND' }, startupCorrectionRecord: { ...record } });
4023
+ } catch {
4024
+ record.state = 'stopped';
4025
+ return stop(guardStop() ?? 'RETRY_REFUSED');
4026
+ }
4027
+ record.retryInvocationId = retry.invocationId;
4028
+ if (retry.attemptOutcome !== 'succeeded') { record.state = 'stopped'; record.stopCode = 'RETRY_FAILED'; }
4029
+ retry.startupCorrection = { ...record };
4030
+ await recordAt(options.runDir, previous.invocationId);
4031
+ await recordAt(runDir, retry.invocationId);
4032
+ return retry;
4033
+ }
4034
+
3919
4035
  // #2915 round 8 (D6, "lazy public fields still differ after mixed use"):
3920
4036
  // `lastAttemptResult`/`lastAttemptInvocationId` are informational
3921
4037
  // bookkeeping only (#2915 step 4 — the retry gate no longer reads them;
@@ -3987,8 +4103,9 @@ export class SessionLauncher extends EventEmitter {
3987
4103
  * `options.correctStartup: { reasonCode, correctionsUsed }` enforces the
3988
4104
  * one-qualified-startup-correction budget on a conductor-declared
3989
4105
  * correction retry (startupCorrectionGate): unqualified causes and second
3990
- * corrections are refused, within the task's existing allowance. There is
3991
- * no automatic correction loop.
4106
+ * corrections are refused, within the task's existing allowance. The only
4107
+ * automatic correction refreshes a changed qualified executable binding
4108
+ * after a launcher-observed ENOENT with confirmed stop, once per task chain.
3992
4109
  *
3993
4110
  * Both checks are gated recovery behavior, not a general launch
3994
4111
  * precondition: enforcement happens entirely INSIDE `launchOneWorker`,
@@ -4015,8 +4132,13 @@ export class SessionLauncher extends EventEmitter {
4015
4132
  // `options.retryOf`-driven and disk-verified every time) is set inside
4016
4133
  // `launchOneWorker` itself, gated on whether recovery actually admitted
4017
4134
  // this attempt (#2915 review 6, finding 6) — never here unconditionally.
4018
- try { return await launchOneWorker(this, options); }
4135
+ directStartupCorrectionContext.delete(this);
4136
+ try {
4137
+ const result = await launchOneWorker(this, options);
4138
+ return await correctDirectWorkerStartup(this, options, result);
4139
+ }
4019
4140
  finally {
4141
+ directStartupCorrectionContext.delete(this);
4020
4142
  _workerRecoverySubscriptions.get(this)?.();
4021
4143
  _workerRecoverySubscriptions.delete(this);
4022
4144
  this.workerLaunchPending = false;