@aiwg/cli 2026.10.10 → 2026.10.12

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 (156) hide show
  1. package/agentic/code/providers/model-capabilities.v1.json +78 -14
  2. package/agentic/code/providers/model-catalog.v1.json +62 -12
  3. package/dist/src/artifacts/binding-target-identity.js +51 -0
  4. package/dist/src/artifacts/checksum-manifest.js +12 -0
  5. package/dist/src/artifacts/index-builder.js +11 -13
  6. package/dist/src/artifacts/query-engine.js +5 -11
  7. package/dist/src/cli/handlers/doctor-help.js +32 -0
  8. package/dist/src/cli/handlers/gates.js +19 -18
  9. package/dist/src/cli/handlers/models-help.js +7 -1
  10. package/dist/src/cli/handlers/models.js +3 -3
  11. package/dist/src/cli/handlers/script-runner.js +8 -5
  12. package/dist/src/cli/handlers/sessions-intake.js +104 -0
  13. package/dist/src/cli/handlers/sessions-tail.js +172 -0
  14. package/dist/src/cli/handlers/sessions.js +287 -31
  15. package/dist/src/cli/handlers/skill-lint.js +10 -3
  16. package/dist/src/cli/handlers/steward-format.js +100 -0
  17. package/dist/src/cli/handlers/steward-models.js +167 -0
  18. package/dist/src/cli/handlers/steward.js +14 -248
  19. package/dist/src/cli/handlers/use-bundle-counts.js +61 -0
  20. package/dist/src/cli/handlers/use.js +18 -28
  21. package/dist/src/cli/handlers/utilities.js +1 -24
  22. package/dist/src/config/aiwg-config.js +7 -0
  23. package/dist/src/config/authorization-cli.js +89 -0
  24. package/dist/src/config/cli-help.js +1 -0
  25. package/dist/src/config/cli.js +10 -1
  26. package/dist/src/config/worker-context.js +337 -0
  27. package/dist/src/decision/context-pruning.js +3 -2
  28. package/dist/src/decision/ensemble/contract.js +15 -4
  29. package/dist/src/decision/ensemble-study/artifacts.js +5 -0
  30. package/dist/src/decision/ensemble-study/score.js +4 -3
  31. package/dist/src/decision/feature-export.js +3 -9
  32. package/dist/src/decision/heldout/collector.js +2 -2
  33. package/dist/src/decision/issue-triage/runtime.js +3 -9
  34. package/dist/src/decision/qualification/gates.js +2 -1
  35. package/dist/src/decision/qualification/release.js +2 -3
  36. package/dist/src/decision/routing/report.js +2 -0
  37. package/dist/src/decision/sdlc-screening.js +3 -5
  38. package/dist/src/decision/sensitivity/comparative.js +5 -3
  39. package/dist/src/gates/driver.js +6 -77
  40. package/dist/src/gates/evaluate.js +4 -11
  41. package/dist/src/gates/floors.js +4 -4
  42. package/dist/src/gates/index.js +3 -3
  43. package/dist/src/gates/lattice.js +26 -0
  44. package/dist/src/gates/registry.js +2 -1
  45. package/dist/src/gates/report.js +19 -7
  46. package/dist/src/issues/audit-cli.js +120 -0
  47. package/dist/src/issues/audit-contract.js +64 -0
  48. package/dist/src/issues/audit-local.js +82 -0
  49. package/dist/src/issues/audit.js +220 -0
  50. package/dist/src/lint/rule-activation.js +15 -1
  51. package/dist/src/mcp/tools/worker-invoke.mjs +108 -10
  52. package/dist/src/mission-protocol/canonical-codecs.js +270 -0
  53. package/dist/src/mission-protocol/codecs.js +22 -550
  54. package/dist/src/mission-protocol/health-trace-codecs.js +281 -0
  55. package/dist/src/mission-protocol/health-trace-types.js +43 -0
  56. package/dist/src/mission-protocol/types.js +2 -42
  57. package/dist/src/mission-protocol/worker-contract-json.js +10 -0
  58. package/dist/src/mission-protocol/worker-review-gate.js +100 -0
  59. package/dist/src/mission-protocol/worker-review-types.js +2 -0
  60. package/dist/src/models/catalog-policy.js +280 -0
  61. package/dist/src/models/index.js +1 -1
  62. package/dist/src/models/model-capabilities.v1.json +78 -14
  63. package/dist/src/models/model-catalog.v1.json +62 -12
  64. package/dist/src/models/policy-resources.js +28 -0
  65. package/dist/src/models/provider-policy.js +10 -539
  66. package/dist/src/models/resolver.js +26 -8
  67. package/dist/src/models/router.js +15 -2
  68. package/dist/src/models/tiers.js +13 -0
  69. package/dist/src/models/types.js +0 -6
  70. package/dist/src/models/worker-ceilings.js +59 -0
  71. package/dist/src/models/worker-invocation-policy.js +243 -0
  72. package/dist/src/models/worker-plan.js +16 -20
  73. package/dist/src/models/worker-review-lineage.js +55 -0
  74. package/dist/src/models/worker-selection-policy.js +1 -1
  75. package/dist/src/models/wrapper-route.js +6 -3
  76. package/dist/src/policy/authorization.js +30 -1
  77. package/dist/src/policy/hitl-config.js +63 -0
  78. package/dist/src/policy/hitl-receipt.js +57 -0
  79. package/dist/src/policy/hitl-types.js +4 -0
  80. package/dist/src/policy/hitl-validation.js +39 -0
  81. package/dist/src/policy/hitl.js +117 -0
  82. package/dist/src/process/aiwg-probe-command.d.mts +15 -0
  83. package/dist/src/process/aiwg-probe-command.mjs +37 -0
  84. package/dist/src/rules/activation.d.mts +1 -1
  85. package/dist/src/rules/activation.mjs +0 -2
  86. package/dist/src/rules/gate-coverage.js +51 -0
  87. package/dist/src/rules/gate-references.js +16 -0
  88. package/dist/src/rules/rule-frontmatter.schema.json +7 -1
  89. package/dist/src/serve/worker-command-request.js +21 -4
  90. package/dist/src/serve/worker-invocation-preparation.js +3 -1
  91. package/dist/src/serve/worker-invocation.js +21 -10
  92. package/dist/src/sessions/adapters/deepseek-harness.js +1 -0
  93. package/dist/src/sessions/adapters/grok-build-native.js +269 -0
  94. package/dist/src/sessions/adapters/grok-build.js +57 -15
  95. package/dist/src/sessions/adapters/grokbot.js +7 -0
  96. package/dist/src/sessions/adapters/hermes-state-db.js +252 -0
  97. package/dist/src/sessions/adapters/hermes.js +42 -3
  98. package/dist/src/sessions/adapters/warp.js +1 -1
  99. package/dist/src/sessions/batch-import.js +44 -6
  100. package/dist/src/sessions/contracts.js +20 -1
  101. package/dist/src/sessions/coverage-preflight.js +7 -1
  102. package/dist/src/sessions/discovery-capability.js +38 -0
  103. package/dist/src/sessions/fortemi-export-mapping.js +33 -3
  104. package/dist/src/sessions/host-catalog.js +157 -0
  105. package/dist/src/sessions/image-blobs.js +141 -0
  106. package/dist/src/sessions/importer.js +9 -232
  107. package/dist/src/sessions/index.js +6 -0
  108. package/dist/src/sessions/intake-dir.js +71 -0
  109. package/dist/src/sessions/normalization.js +251 -0
  110. package/dist/src/sessions/refresh.js +25 -2
  111. package/dist/src/sessions/repository.js +164 -22
  112. package/dist/src/sessions/tail.js +91 -0
  113. package/dist/src/sessions/workspace-discovery.js +88 -9
  114. package/dist/src/skills/agent-skill-sidecar.js +28 -0
  115. package/dist/src/skills/agent-skills.js +4 -23
  116. package/dist/src/skills/claude-surface-projection.js +25 -0
  117. package/dist/src/skills/deployer.js +8 -288
  118. package/dist/src/skills/deployment-policy.js +114 -0
  119. package/dist/src/skills/deployment-projection.js +152 -0
  120. package/dist/src/skills/deployment-types.js +20 -0
  121. package/dist/src/skills/exporter.js +8 -31
  122. package/dist/src/skills/provider-extensions.js +41 -0
  123. package/dist/src/skills/run.js +6 -0
  124. package/dist/src/skills/types.js +0 -8
  125. package/dist/src/skills/validator-resources.js +68 -0
  126. package/dist/src/skills/validator.js +6 -55
  127. package/dist/src/storage/cli-migrate-options.js +125 -0
  128. package/dist/src/storage/cli-migrate.js +111 -0
  129. package/dist/src/storage/cli.js +4 -279
  130. package/dist/src/storage/migration-adapter.js +79 -0
  131. package/dist/src/storage/migration-copy.js +108 -0
  132. package/dist/src/storage/migration-protocol.js +14 -197
  133. package/dist/src/storage/migration-records.js +186 -0
  134. package/dist/src/storage/migration-types.js +12 -0
  135. package/dist/src/tracker/forge-http.js +2 -1
  136. package/dist/src/tracker/issue-pages.js +110 -0
  137. package/package.json +7 -1
  138. package/schemas/mission-v1.schema.json +335 -0
  139. package/schemas/models/model-catalog.v1.schema.json +19 -1
  140. package/schemas/models/model-config-compatibility.v1.schema.json +3 -2
  141. package/schemas/models/model-policy.v1.schema.json +1 -1
  142. package/tools/agents/providers/provider-paths-staging.mjs +35 -0
  143. package/tools/agents/providers/rule-binding-targets.mjs +146 -0
  144. package/tools/agents/providers/rule-delivery.mjs +7 -126
  145. package/tools/ralph-external/lib/muse-adapter.mjs +10 -0
  146. package/tools/ralph-external/lib/worker-context.mjs +237 -0
  147. package/tools/ralph-external/lib/worker-invocation-ancestry.mjs +132 -0
  148. package/tools/ralph-external/lib/worker-invocation-processes.mjs +123 -0
  149. package/tools/ralph-external/lib/worker-isolation-status.mjs +48 -0
  150. package/tools/ralph-external/lib/worker-isolation.mjs +17 -29
  151. package/tools/ralph-external/lib/worker-launch-cli.mjs +3 -1
  152. package/tools/ralph-external/lib/worker-overflow.mjs +48 -13
  153. package/tools/ralph-external/lib/worker-retry-evidence.mjs +103 -0
  154. package/tools/ralph-external/lib/worker-termination.mjs +82 -0
  155. package/tools/ralph-external/session-launcher.mjs +51 -96
  156. package/vscode-extension/schemas/aiwg.config.v1.json +177 -0
@@ -3,13 +3,14 @@ function jsonResult(value, exitCode = 0) {
3
3
  }
4
4
  function usage() {
5
5
  return [
6
- 'Usage: aiwg gates <validate|evaluate|show|list> [options]',
6
+ 'Usage: aiwg gates <validate|evaluate|show|list|coverage> [options]',
7
7
  '',
8
8
  'Commands:',
9
9
  ' validate <pack|binding|report> <path> [--pack-dir <dir>] [--aiwg-root <dir>]',
10
10
  ' evaluate --binding <file> --metrics <file> --holdout <file> [--upstream <file>] --now <iso> --trusted-binding-digest <sha256> [--pack-dir <dir>] [--aiwg-root <dir>]',
11
11
  ' show <id> [--pack-dir <dir>] [--aiwg-root <dir>]',
12
12
  ' list [--namespace <ns>] [--rule [<rule-id>]] [--pack-dir <dir>] [--aiwg-root <dir>]',
13
+ ' coverage [--rule <rule-id>] [--json] [--pack-dir <dir>] [--aiwg-root <dir>]',
13
14
  '',
14
15
  'Packs live in <bundle>/gate-packs/*.gatepack.yaml|json and register as',
15
16
  'aiwg:<bundle>/<name> (shipped).',
@@ -77,7 +78,7 @@ async function executeGates(ctx) {
77
78
  try {
78
79
  if (!command || command === '--help' || command === '-h')
79
80
  return { exitCode: 0, message: usage(), rawOutput: true };
80
- const { validateGateFile, evaluateGatesFromFiles, showGatePack, listGatePacks, packsReferencedByRule, } = await import('../../gates/driver.js');
81
+ const { validateGateFile, evaluateGatesFromFiles, showGatePack, listGatePacks, } = await import('../../gates/driver.js');
81
82
  const options = driverOptions(ctx);
82
83
  if (command === 'validate') {
83
84
  const args = positionals(ctx.args, 'validate');
@@ -115,27 +116,27 @@ async function executeGates(ctx) {
115
116
  return { exitCode: 2, message: `gates show requires <id>\n\n${usage()}`, rawOutput: true };
116
117
  return jsonResult(showGatePack(id, options), 0);
117
118
  }
118
- if (command === 'list') {
119
+ if (command === 'coverage' || command === 'list' && ctx.args.includes('--rule')) {
120
+ const { listRuleGateCoverage, formatRuleGateCoverage } = await import('../../rules/gate-coverage.js');
121
+ const value = takeOption(ctx.args, '--rule');
122
+ const rule = value && !value.startsWith('-') ? value : undefined;
123
+ const rules = listRuleGateCoverage(options, rule);
124
+ if (command === 'coverage') {
125
+ return ctx.args.includes('--json') ? jsonResult({ schema: 'aiwg-gates-coverage/v1', rules })
126
+ : { exitCode: 0, message: formatRuleGateCoverage(rules), rawOutput: true };
127
+ }
119
128
  const namespace = takeOption(ctx.args, '--namespace');
120
- const ruleIndex = ctx.args.indexOf('--rule');
121
- const ruleValue = ruleIndex >= 0 ? ctx.args[ruleIndex + 1] : undefined;
122
- const rule = ruleIndex >= 0 ? (ruleValue && !ruleValue.startsWith('-') ? ruleValue : null) : undefined;
123
129
  const packs = listGatePacks({ ...options, ...(namespace ? { namespace } : {}) });
124
- if (rule === undefined)
125
- return jsonResult({ schema: 'aiwg-gates-list/v1', packs }, 0);
126
- if (rule === null) {
127
- return jsonResult({
128
- schema: 'aiwg-gates-list/v1', packs,
129
- enforcedBy: { status: 'stub', note: 'Rule enforcedBy coverage is tracked in #2839; rules without enforcedBy remain guidance.' },
130
- }, 0);
131
- }
132
- const referenced = new Set(await packsReferencedByRule(rule, options));
130
+ const referenced = new Set(rules.flatMap(item => item.enforcedBy.map(reference => reference.split('#')[0])));
133
131
  return jsonResult({
134
- schema: 'aiwg-gates-list/v1', rule,
135
- packs: packs.filter(pack => referenced.has(pack.id)),
136
- enforcedBy: { status: 'stub', note: 'Rule enforcedBy coverage is tracked in #2839.' },
132
+ schema: 'aiwg-gates-list/v1', ...(rule ? { rule } : {}), rules,
133
+ packs: rule ? packs.filter(pack => referenced.has(pack.id)) : packs,
137
134
  }, 0);
138
135
  }
136
+ if (command === 'list') {
137
+ const namespace = takeOption(ctx.args, '--namespace');
138
+ return jsonResult({ schema: 'aiwg-gates-list/v1', packs: listGatePacks({ ...options, ...(namespace ? { namespace } : {}) }) });
139
+ }
139
140
  return { exitCode: 2, message: usage(), rawOutput: true };
140
141
  }
141
142
  catch (error) {
@@ -27,7 +27,7 @@ Artifact selectors and options:
27
27
  --drift Include catalog drift for sources/refresh
28
28
  --url <url> Remote catalog URL for refresh
29
29
  --help, -h Show this reference
30
- TIER: economy | standard | premium | max-quality
30
+ TIER: economy | standard | premium | flagship (alias: max-quality)
31
31
 
32
32
  Direct worker resolution (plans an invocation; does not launch it):
33
33
  aiwg models resolve --role <role> --task <class> --group <group>
@@ -43,6 +43,12 @@ Direct worker resolution (plans an invocation; does not launch it):
43
43
  --transport Must match group execution transport (default: shell)
44
44
  --format JSON plan or POSIX shell command (default: json)
45
45
  --json Alias for --format json
46
+ --max-attempts <count> Tighten total attempts, including the initial attempt
47
+ --timeout-ms <ms> Tighten the hard stop; positive multiple of 1000 (whole seconds)
48
+ --max-output-tokens <count> Tighten the existing task token budget (tokens)
49
+ --max-usd <amount> Tighten the task cost ceiling (costUsd)
50
+ Limits may only tighten resolved policy; larger values fail
51
+ JSON includes ceilings and their origins
46
52
  --authorize-premium Authorize premium for this resolution only
47
53
  Agents require explicit human confirmation of escalation
48
54
 
@@ -144,7 +144,7 @@ async function resolveDirect(ctx, services) {
144
144
  return { exitCode: 2 };
145
145
  }
146
146
  }
147
- const TIERS = new Set(['economy', 'standard', 'premium', 'max-quality']);
147
+ const TIERS = new Set(['economy', 'standard', 'premium', 'flagship', 'max-quality']);
148
148
  const PROVIDERS = new Set(Object.keys(loadProviderModelCapabilities().providers));
149
149
  function parseArgs(args) {
150
150
  const [subcommand = 'help', ...rest] = args;
@@ -241,7 +241,7 @@ function select(artifacts, parsed, useTierSelector = true) {
241
241
  && (!framework || item.framework === framework)));
242
242
  }
243
243
  function normalizedTier(tier) {
244
- return tier === 'max-quality' ? 'premium' : tier ?? 'standard';
244
+ return tier === 'max-quality' ? 'flagship' : tier ?? 'standard';
245
245
  }
246
246
  function resolved(item, provider, catalog) {
247
247
  const policy = {
@@ -331,7 +331,7 @@ async function execute(ctx, services = { load: loadWorkerInputs }) {
331
331
  return { exitCode: 0 };
332
332
  }
333
333
  // Preserve artifact/native resolve when direct-only flags are absent.
334
- if (ctx.args[0] === 'resolve' && ctx.args.slice(1).some(arg => /^--(?:task|group|brief|workdir|final-output|transport|format|authorize-premium)(?:=|$)/.test(arg))) {
334
+ if (ctx.args[0] === 'resolve' && ctx.args.slice(1).some(arg => /^--(?:task|group|brief|workdir|final-output|transport|format|authorize-premium|max-attempts|timeout-ms|max-output-tokens|max-usd)(?:=|$)/.test(arg))) {
335
335
  return resolveDirect(ctx, services);
336
336
  }
337
337
  if (await handleWorkerAdoptionArgs(ctx.args, ctx.cwd, 'models', ctx.signal, ctx.dryRun))
@@ -120,11 +120,13 @@ export class DefaultScriptRunner {
120
120
  clearTimeout(timeoutId);
121
121
  if (killEscalationId)
122
122
  clearTimeout(killEscalationId);
123
- // On failure in capture mode, surface the captured stderr
124
- if (options.capture && code !== 0 && capturedStderrBytes > 0) {
123
+ // On failure in capture mode, surface the captured stderr. Every
124
+ // failure carries a message so callers never print an empty ERROR.
125
+ if (options.capture && code !== 0) {
126
+ const stderr = Buffer.concat(capturedStderr).toString().trim();
125
127
  resolve({
126
128
  exitCode: code ?? 1,
127
- message: Buffer.concat(capturedStderr).toString().trim(),
129
+ message: stderr || `Script exited with code ${code ?? 1}: ${path.basename(fullPath)}`,
128
130
  });
129
131
  }
130
132
  else {
@@ -183,8 +185,9 @@ export class DefaultScriptRunner {
183
185
  // the runner still supports arbitrary scripts via its subprocess path.
184
186
  const deployer = await import(pathToFileURL(fullPath).href);
185
187
  const exitCode = (await deployer.main(args, { capture: options.capture === true })) ?? 0;
186
- const message = Buffer.concat(diagnostics).toString().trim();
187
- return exitCode && message ? { exitCode, message } : { exitCode };
188
+ const message = Buffer.concat(diagnostics).toString().trim()
189
+ || (exitCode ? `Deployment script exited with code ${exitCode}: ${path.basename(fullPath)}` : '');
190
+ return exitCode || message ? { exitCode, message } : { exitCode };
188
191
  }
189
192
  catch (error) {
190
193
  const message = Buffer.concat(diagnostics).toString().trim()
@@ -0,0 +1,104 @@
1
+ // `aiwg sessions import --from-dir`: idempotent intake-directory batch import (#3168).
2
+ import { join } from 'node:path';
3
+ import { fingerprintSourceFile, intakeSourceId, isRecognisedIntakeFile, listIntakeDirFiles, summarizeIntakeReceipts, INTAKE_DIR_SCHEMA_VERSION, } from '../../sessions/index.js';
4
+ export async function importFromDir(ctx, args, dir, deps) {
5
+ const { assertSupportedImportProvider, normalizeWorkspaceId, isDryRun, preview, ok, openRepository, adapterVersionFor, importSource, importFailureReason, } = deps;
6
+ const provider = assertSupportedImportProvider(args.values.get('--provider') ?? 'generic');
7
+ const workspaceId = args.values.has('--workspace')
8
+ ? normalizeWorkspaceId(ctx.cwd, args.values.get('--workspace'))
9
+ : 'default';
10
+ const prefix = args.values.get('--source-id');
11
+ const files = await listIntakeDirFiles(dir);
12
+ if (isDryRun(ctx, args)) {
13
+ return preview('import', {
14
+ fromDir: dir,
15
+ provider,
16
+ workspaceId,
17
+ sourceIdPrefix: prefix ?? null,
18
+ files: files.map((file) => {
19
+ const recognition = isRecognisedIntakeFile(provider, file);
20
+ return {
21
+ file,
22
+ sourceId: recognition.recognised ? intakeSourceId(prefix, file) : null,
23
+ recognised: recognition.recognised,
24
+ reason: recognition.reason ?? null,
25
+ };
26
+ }),
27
+ wouldPersist: false,
28
+ });
29
+ }
30
+ const repository = openRepository(ctx, args);
31
+ try {
32
+ const interrupted = new Map();
33
+ const receipts = [];
34
+ for (const file of files) {
35
+ const recognition = isRecognisedIntakeFile(provider, file);
36
+ const input = join(dir, file);
37
+ if (!recognition.recognised) {
38
+ receipts.push({
39
+ file, sourceId: null, digest: null, status: 'skipped',
40
+ reason: recognition.reason ?? 'unrecognised file', sessionsInserted: 0, eventsInserted: 0,
41
+ });
42
+ continue;
43
+ }
44
+ const sourceId = intakeSourceId(prefix, file);
45
+ let digest = null;
46
+ try {
47
+ const fingerprint = await fingerprintSourceFile({ selectedPath: input, allowedRoots: [dir] });
48
+ digest = fingerprint.digest;
49
+ const checkpoint = repository.getCheckpoint(sourceId, adapterVersionFor(provider));
50
+ if (checkpoint?.prefixDigest === digest && checkpoint.sourceSize === fingerprint.size) {
51
+ receipts.push({
52
+ file, sourceId, digest, status: 'already-imported',
53
+ reason: 'content digest already imported', sessionsInserted: 0, eventsInserted: 0,
54
+ });
55
+ continue;
56
+ }
57
+ }
58
+ catch (error) {
59
+ receipts.push({
60
+ file, sourceId, digest, status: 'rejected',
61
+ reason: importFailureReason(error), sessionsInserted: 0, eventsInserted: 0,
62
+ });
63
+ continue;
64
+ }
65
+ const fileValues = new Map(args.values);
66
+ fileValues.delete('--from-dir');
67
+ fileValues.set('--source-id', sourceId);
68
+ try {
69
+ const result = await importSource(ctx, {
70
+ command: 'import', positionals: [input], flags: new Set(args.flags),
71
+ values: fileValues, codexRoots: args.codexRoots, exclude: args.exclude,
72
+ });
73
+ const data = result.envelope.data;
74
+ for (const run of data.interruptedRuns ?? [])
75
+ interrupted.set(run.runId, run);
76
+ receipts.push({
77
+ file, sourceId, digest, status: 'imported', reason: null,
78
+ sessionsInserted: data.totals?.sessionsInserted ?? 0,
79
+ eventsInserted: data.totals?.eventsInserted ?? 0,
80
+ });
81
+ }
82
+ catch (error) {
83
+ receipts.push({
84
+ file, sourceId, digest, status: 'rejected',
85
+ reason: importFailureReason(error), sessionsInserted: 0, eventsInserted: 0,
86
+ });
87
+ }
88
+ }
89
+ return ok('import', {
90
+ fromDir: dir,
91
+ provider,
92
+ workspaceId,
93
+ sourceIdPrefix: prefix ?? null,
94
+ schemaVersion: INTAKE_DIR_SCHEMA_VERSION,
95
+ files: receipts,
96
+ summary: summarizeIntakeReceipts(receipts),
97
+ interruptedRuns: [...interrupted.values()],
98
+ });
99
+ }
100
+ finally {
101
+ repository.close();
102
+ }
103
+ }
104
+ //# sourceMappingURL=sessions-intake.js.map
@@ -0,0 +1,172 @@
1
+ // Resolve catalog identities and authorized native paths for the sessions tail verb.
2
+ import { existsSync } from 'node:fs';
3
+ import { dirname, isAbsolute, resolve } from 'node:path';
4
+ import { readAiwgConfig } from '../../config/aiwg-config.js';
5
+ import { createSessionAdapterRegistry } from '../../sessions/adapter-registry.js';
6
+ import { assertSessionProviderId, SessionContractError } from '../../sessions/contracts.js';
7
+ import { redactSessionValue } from '../../sessions/policy.js';
8
+ import { resolveImportLimits } from '../../sessions/import-limits.js';
9
+ import { acquireImportLease } from '../../sessions/import-lease.js';
10
+ import { SessionRepository } from '../../sessions/repository.js';
11
+ import { assertTailSupported, tailSession } from '../../sessions/tail.js';
12
+ import { defaultDiscoveryManifestPath, readDiscoveryManifest } from '../../sessions/workspace-discovery.js';
13
+ export async function executeSessionsTail(ctx, args, helpers) {
14
+ const json = args.flags.has('--json');
15
+ let repository;
16
+ let lease;
17
+ const controller = new AbortController();
18
+ const stop = () => controller.abort();
19
+ const signal = ctx.signal ? AbortSignal.any([ctx.signal, controller.signal]) : controller.signal;
20
+ process.on('SIGINT', stop);
21
+ try {
22
+ const latest = args.flags.has('--latest');
23
+ const reference = args.positionals[0];
24
+ const providerInput = args.values.get('--provider');
25
+ if (args.positionals.length > 1 || (latest ? !providerInput || !!reference : !reference)) {
26
+ throw new SessionContractError('INVALID_ARGUMENT', 'tail requires <session-id> or --provider <p> --latest');
27
+ }
28
+ if (args.flags.has('--import') && (ctx.dryRun || args.flags.has('--dry-run'))) {
29
+ throw new SessionContractError('INVALID_ARGUMENT', 'tail --import cannot be combined with --dry-run');
30
+ }
31
+ const provider = providerInput ? assertSessionProviderId(providerInput) : undefined;
32
+ if (provider)
33
+ assertTailSupported(createSessionAdapterRegistry().get(provider));
34
+ let database = helpers.databasePath(ctx, args);
35
+ repository = new SessionRepository(database, { readonly: true });
36
+ const workspace = args.values.has('--workspace') || latest
37
+ ? helpers.readAuthorizationContext(ctx, args, 'tail', repository).workspaceId : undefined;
38
+ let matches = latest
39
+ ? repository.listSessions({ provider, workspaceId: workspace, limit: 1 }).items
40
+ : repository.findSessionsByReference(reference, workspace);
41
+ if (provider)
42
+ matches = matches.filter(session => session.provider === provider);
43
+ // Account-scoped providers live in the referenced host catalog (#3232).
44
+ const references = repository.listHostSessionReferences().filter(item => !provider || item.provider === provider);
45
+ const hosts = new Set(references.map(item => item.hostCatalog));
46
+ const candidates = matches.map(session => ({ session, database }));
47
+ for (const hostPath of hosts) {
48
+ if (!existsSync(hostPath))
49
+ continue;
50
+ const host = new SessionRepository(hostPath, { readonly: true });
51
+ try {
52
+ const allowed = new Set(references.filter(item => item.hostCatalog === hostPath)
53
+ .map(item => item.sessionId));
54
+ const sessions = latest
55
+ ? references.filter(item => item.hostCatalog === hostPath)
56
+ .flatMap(item => host.getSession(item.sessionId) ?? [])
57
+ : host.findSessionsByReference(reference);
58
+ candidates.push(...sessions.filter(session => allowed.has(session.sessionId))
59
+ .map(session => ({ session, database: hostPath })));
60
+ }
61
+ finally {
62
+ host.close();
63
+ }
64
+ }
65
+ const selected = latest
66
+ ? candidates.sort((a, b) => (b.session.updatedAt ?? '').localeCompare(a.session.updatedAt ?? '')
67
+ || b.session.sessionId.localeCompare(a.session.sessionId)).slice(0, 1)
68
+ : candidates;
69
+ if (selected.length !== 1) {
70
+ throw new SessionContractError('INVALID_ARGUMENT', selected.length
71
+ ? 'session reference is ambiguous; select --workspace or an internal session ID'
72
+ : 'session not found in the selected catalog and workspace');
73
+ }
74
+ const session = selected[0].session;
75
+ if (selected[0].database !== database) {
76
+ repository.close();
77
+ database = selected[0].database;
78
+ repository = new SessionRepository(database, { readonly: true });
79
+ }
80
+ const source = repository.getSource(session.sourceId);
81
+ if (!source)
82
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'catalog source is unavailable');
83
+ const config = await readAiwgConfig(isAbsolute(session.workspaceId) ? session.workspaceId : ctx.cwd);
84
+ const limits = resolveImportLimits(config?.sessions?.import, {
85
+ maxRecordBytes: args.values.get('--max-record-bytes'),
86
+ maxSourceBytes: args.values.get('--max-source-bytes'),
87
+ });
88
+ const adapter = createSessionAdapterRegistry(limits).get(session.provider);
89
+ assertTailSupported(adapter);
90
+ const selectedSource = await resolveTailSource(ctx, args, session, source.locatorClass, config?.sessions?.discovery_manifests ?? [], resolve(dirname(helpers.databasePath(ctx, args)), 'discovery-manifest.json'));
91
+ const importing = args.flags.has('--import');
92
+ if (importing) {
93
+ repository.close();
94
+ repository = undefined;
95
+ lease = await acquireImportLease(database, `sessions-tail-${process.pid}`);
96
+ repository = new SessionRepository(database);
97
+ }
98
+ const checkpoint = repository.getCheckpoint(source.sourceId, adapter.adapterVersion);
99
+ await tailSession({
100
+ source, selectedSource, adapter, repository, sessionId: session.sessionId,
101
+ workspaceId: session.workspaceId, policyVersion: checkpoint?.policyVersion ?? '1.0.0',
102
+ limits, signal, follow: args.flags.has('--follow'), import: importing,
103
+ onEvent: event => {
104
+ if (json)
105
+ console.log(JSON.stringify({ contractVersion: '1.0.0', command: 'sessions.tail',
106
+ status: 'ok', data: { event }, error: null }));
107
+ else
108
+ console.log(`${event.occurredAt ?? '-'} ${session.provider} ${event.kind}: ${event.searchableText}`);
109
+ },
110
+ onPoll: flushStdout,
111
+ });
112
+ await flushStdout();
113
+ return { exitCode: 0 };
114
+ }
115
+ catch (error) {
116
+ if (signal.aborted)
117
+ return { exitCode: 0 };
118
+ const cliError = error instanceof Error && 'code' in error && 'exitCode' in error
119
+ ? error : undefined;
120
+ const code = error instanceof SessionContractError ? error.code
121
+ : cliError ? cliError.code
122
+ : 'code' in Object(error) && error.code === 'IMPORT_LOCKED'
123
+ ? 'IMPORT_LOCKED' : 'CATALOG_UNAVAILABLE';
124
+ const message = redactSessionValue(error instanceof Error ? error.message : 'session tail failed').value;
125
+ if (json) {
126
+ console.log(JSON.stringify({ contractVersion: '1.0.0', command: 'sessions.tail', status: 'error',
127
+ data: null, error: { code, message } }));
128
+ await flushStdout();
129
+ }
130
+ else
131
+ console.error(`${code}: ${message}`);
132
+ return { exitCode: cliError?.exitCode ?? (code === 'UNSUPPORTED_PROVIDER' ? 3 : code === 'INVALID_ARGUMENT' ? 2
133
+ : code === 'IMPORT_LOCKED' ? 7 : code === 'CATALOG_UNAVAILABLE' ? 6 : 5), message };
134
+ }
135
+ finally {
136
+ process.removeListener('SIGINT', stop);
137
+ repository?.close();
138
+ await lease?.release();
139
+ }
140
+ }
141
+ async function resolveTailSource(ctx, args, session, locatorClass, configured, catalogManifest) {
142
+ const workspace = isAbsolute(session.workspaceId) ? session.workspaceId : ctx.cwd;
143
+ // Existing explicit input option also supports older direct imports, whose
144
+ // catalog intentionally never retained the unredacted native pathname.
145
+ const input = args.values.get('--input');
146
+ if (input) {
147
+ const locator = resolve(ctx.cwd, input);
148
+ return { provider: session.provider, sourceId: session.sourceId, nativeSessionId: session.nativeSessionId, locatorClass, locator,
149
+ authorizedScope: { workspaceId: session.workspaceId, allowedRoots: [dirname(locator)] } };
150
+ }
151
+ const paths = args.values.has('--manifest') ? [resolve(ctx.cwd, args.values.get('--manifest'))] : [
152
+ defaultDiscoveryManifestPath(workspace), defaultDiscoveryManifestPath(ctx.cwd), catalogManifest,
153
+ ...configured.map(path => resolve(workspace, path)),
154
+ ];
155
+ for (const path of new Set(paths)) {
156
+ if (!existsSync(path))
157
+ continue;
158
+ const manifest = await readDiscoveryManifest(path);
159
+ const source = manifest.sources.find(item => item.sourceId === session.sourceId
160
+ && item.provider === session.provider && item.locatorClass === locatorClass && item.status !== 'excluded');
161
+ if (source)
162
+ return { provider: session.provider, sourceId: session.sourceId, nativeSessionId: session.nativeSessionId, locatorClass,
163
+ locator: source.locator, authorizedScope: {
164
+ workspaceId: session.workspaceId, allowedRoots: [source.authorizedRoot],
165
+ } };
166
+ }
167
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'native source location is unavailable; pass its saved --manifest or explicitly authorize --input <native-source>');
168
+ }
169
+ function flushStdout() {
170
+ return new Promise((resolve, reject) => process.stdout.write('', error => error ? reject(error) : resolve()));
171
+ }
172
+ //# sourceMappingURL=sessions-tail.js.map