@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.
- package/agentic/code/providers/model-capabilities.v1.json +78 -14
- package/agentic/code/providers/model-catalog.v1.json +62 -12
- package/dist/src/artifacts/binding-target-identity.js +51 -0
- package/dist/src/artifacts/checksum-manifest.js +12 -0
- package/dist/src/artifacts/index-builder.js +11 -13
- package/dist/src/artifacts/query-engine.js +5 -11
- package/dist/src/cli/handlers/doctor-help.js +32 -0
- package/dist/src/cli/handlers/gates.js +19 -18
- package/dist/src/cli/handlers/models-help.js +7 -1
- package/dist/src/cli/handlers/models.js +3 -3
- package/dist/src/cli/handlers/script-runner.js +8 -5
- package/dist/src/cli/handlers/sessions-intake.js +104 -0
- package/dist/src/cli/handlers/sessions-tail.js +172 -0
- package/dist/src/cli/handlers/sessions.js +287 -31
- package/dist/src/cli/handlers/skill-lint.js +10 -3
- package/dist/src/cli/handlers/steward-format.js +100 -0
- package/dist/src/cli/handlers/steward-models.js +167 -0
- package/dist/src/cli/handlers/steward.js +14 -248
- package/dist/src/cli/handlers/use-bundle-counts.js +61 -0
- package/dist/src/cli/handlers/use.js +18 -28
- package/dist/src/cli/handlers/utilities.js +1 -24
- package/dist/src/config/aiwg-config.js +7 -0
- package/dist/src/config/authorization-cli.js +89 -0
- package/dist/src/config/cli-help.js +1 -0
- package/dist/src/config/cli.js +10 -1
- package/dist/src/config/worker-context.js +337 -0
- package/dist/src/decision/context-pruning.js +3 -2
- package/dist/src/decision/ensemble/contract.js +15 -4
- package/dist/src/decision/ensemble-study/artifacts.js +5 -0
- package/dist/src/decision/ensemble-study/score.js +4 -3
- package/dist/src/decision/feature-export.js +3 -9
- package/dist/src/decision/heldout/collector.js +2 -2
- package/dist/src/decision/issue-triage/runtime.js +3 -9
- package/dist/src/decision/qualification/gates.js +2 -1
- package/dist/src/decision/qualification/release.js +2 -3
- package/dist/src/decision/routing/report.js +2 -0
- package/dist/src/decision/sdlc-screening.js +3 -5
- package/dist/src/decision/sensitivity/comparative.js +5 -3
- package/dist/src/gates/driver.js +6 -77
- package/dist/src/gates/evaluate.js +4 -11
- package/dist/src/gates/floors.js +4 -4
- package/dist/src/gates/index.js +3 -3
- package/dist/src/gates/lattice.js +26 -0
- package/dist/src/gates/registry.js +2 -1
- package/dist/src/gates/report.js +19 -7
- package/dist/src/issues/audit-cli.js +120 -0
- package/dist/src/issues/audit-contract.js +64 -0
- package/dist/src/issues/audit-local.js +82 -0
- package/dist/src/issues/audit.js +220 -0
- package/dist/src/lint/rule-activation.js +15 -1
- package/dist/src/mcp/tools/worker-invoke.mjs +108 -10
- package/dist/src/mission-protocol/canonical-codecs.js +270 -0
- package/dist/src/mission-protocol/codecs.js +22 -550
- package/dist/src/mission-protocol/health-trace-codecs.js +281 -0
- package/dist/src/mission-protocol/health-trace-types.js +43 -0
- package/dist/src/mission-protocol/types.js +2 -42
- package/dist/src/mission-protocol/worker-contract-json.js +10 -0
- package/dist/src/mission-protocol/worker-review-gate.js +100 -0
- package/dist/src/mission-protocol/worker-review-types.js +2 -0
- package/dist/src/models/catalog-policy.js +280 -0
- package/dist/src/models/index.js +1 -1
- package/dist/src/models/model-capabilities.v1.json +78 -14
- package/dist/src/models/model-catalog.v1.json +62 -12
- package/dist/src/models/policy-resources.js +28 -0
- package/dist/src/models/provider-policy.js +10 -539
- package/dist/src/models/resolver.js +26 -8
- package/dist/src/models/router.js +15 -2
- package/dist/src/models/tiers.js +13 -0
- package/dist/src/models/types.js +0 -6
- package/dist/src/models/worker-ceilings.js +59 -0
- package/dist/src/models/worker-invocation-policy.js +243 -0
- package/dist/src/models/worker-plan.js +16 -20
- package/dist/src/models/worker-review-lineage.js +55 -0
- package/dist/src/models/worker-selection-policy.js +1 -1
- package/dist/src/models/wrapper-route.js +6 -3
- package/dist/src/policy/authorization.js +30 -1
- package/dist/src/policy/hitl-config.js +63 -0
- package/dist/src/policy/hitl-receipt.js +57 -0
- package/dist/src/policy/hitl-types.js +4 -0
- package/dist/src/policy/hitl-validation.js +39 -0
- package/dist/src/policy/hitl.js +117 -0
- package/dist/src/process/aiwg-probe-command.d.mts +15 -0
- package/dist/src/process/aiwg-probe-command.mjs +37 -0
- package/dist/src/rules/activation.d.mts +1 -1
- package/dist/src/rules/activation.mjs +0 -2
- package/dist/src/rules/gate-coverage.js +51 -0
- package/dist/src/rules/gate-references.js +16 -0
- package/dist/src/rules/rule-frontmatter.schema.json +7 -1
- package/dist/src/serve/worker-command-request.js +21 -4
- package/dist/src/serve/worker-invocation-preparation.js +3 -1
- package/dist/src/serve/worker-invocation.js +21 -10
- package/dist/src/sessions/adapters/deepseek-harness.js +1 -0
- package/dist/src/sessions/adapters/grok-build-native.js +269 -0
- package/dist/src/sessions/adapters/grok-build.js +57 -15
- package/dist/src/sessions/adapters/grokbot.js +7 -0
- package/dist/src/sessions/adapters/hermes-state-db.js +252 -0
- package/dist/src/sessions/adapters/hermes.js +42 -3
- package/dist/src/sessions/adapters/warp.js +1 -1
- package/dist/src/sessions/batch-import.js +44 -6
- package/dist/src/sessions/contracts.js +20 -1
- package/dist/src/sessions/coverage-preflight.js +7 -1
- package/dist/src/sessions/discovery-capability.js +38 -0
- package/dist/src/sessions/fortemi-export-mapping.js +33 -3
- package/dist/src/sessions/host-catalog.js +157 -0
- package/dist/src/sessions/image-blobs.js +141 -0
- package/dist/src/sessions/importer.js +9 -232
- package/dist/src/sessions/index.js +6 -0
- package/dist/src/sessions/intake-dir.js +71 -0
- package/dist/src/sessions/normalization.js +251 -0
- package/dist/src/sessions/refresh.js +25 -2
- package/dist/src/sessions/repository.js +164 -22
- package/dist/src/sessions/tail.js +91 -0
- package/dist/src/sessions/workspace-discovery.js +88 -9
- package/dist/src/skills/agent-skill-sidecar.js +28 -0
- package/dist/src/skills/agent-skills.js +4 -23
- package/dist/src/skills/claude-surface-projection.js +25 -0
- package/dist/src/skills/deployer.js +8 -288
- package/dist/src/skills/deployment-policy.js +114 -0
- package/dist/src/skills/deployment-projection.js +152 -0
- package/dist/src/skills/deployment-types.js +20 -0
- package/dist/src/skills/exporter.js +8 -31
- package/dist/src/skills/provider-extensions.js +41 -0
- package/dist/src/skills/run.js +6 -0
- package/dist/src/skills/types.js +0 -8
- package/dist/src/skills/validator-resources.js +68 -0
- package/dist/src/skills/validator.js +6 -55
- package/dist/src/storage/cli-migrate-options.js +125 -0
- package/dist/src/storage/cli-migrate.js +111 -0
- package/dist/src/storage/cli.js +4 -279
- package/dist/src/storage/migration-adapter.js +79 -0
- package/dist/src/storage/migration-copy.js +108 -0
- package/dist/src/storage/migration-protocol.js +14 -197
- package/dist/src/storage/migration-records.js +186 -0
- package/dist/src/storage/migration-types.js +12 -0
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/tracker/issue-pages.js +110 -0
- package/package.json +7 -1
- package/schemas/mission-v1.schema.json +335 -0
- package/schemas/models/model-catalog.v1.schema.json +19 -1
- package/schemas/models/model-config-compatibility.v1.schema.json +3 -2
- package/schemas/models/model-policy.v1.schema.json +1 -1
- package/tools/agents/providers/provider-paths-staging.mjs +35 -0
- package/tools/agents/providers/rule-binding-targets.mjs +146 -0
- package/tools/agents/providers/rule-delivery.mjs +7 -126
- package/tools/ralph-external/lib/muse-adapter.mjs +10 -0
- package/tools/ralph-external/lib/worker-context.mjs +237 -0
- package/tools/ralph-external/lib/worker-invocation-ancestry.mjs +132 -0
- package/tools/ralph-external/lib/worker-invocation-processes.mjs +123 -0
- package/tools/ralph-external/lib/worker-isolation-status.mjs +48 -0
- package/tools/ralph-external/lib/worker-isolation.mjs +17 -29
- package/tools/ralph-external/lib/worker-launch-cli.mjs +3 -1
- package/tools/ralph-external/lib/worker-overflow.mjs +48 -13
- package/tools/ralph-external/lib/worker-retry-evidence.mjs +103 -0
- package/tools/ralph-external/lib/worker-termination.mjs +82 -0
- package/tools/ralph-external/session-launcher.mjs +51 -96
- 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,
|
|
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
|
-
|
|
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' ? '
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|