@aiwg/cli 2026.8.0 → 2026.8.2

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 (70) hide show
  1. package/README.md +33 -0
  2. package/agentic/code/providers/capability-matrix.yaml +511 -0
  3. package/agentic/code/providers/model-capabilities.v1.json +120 -0
  4. package/agentic/code/providers/model-catalog.v1.json +96 -0
  5. package/agentic/code/providers/model-policy-evaluations.v1.json +50 -0
  6. package/agentic/code/providers/premium-model-allowlist.v1.json +36 -0
  7. package/bin/aiwg.mjs +14 -10
  8. package/dist/src/api/index.d.ts +1 -0
  9. package/dist/src/api/index.js +1 -0
  10. package/dist/src/artifacts/cli.js +2 -0
  11. package/dist/src/artifacts/types.js +4 -0
  12. package/dist/src/auth/client.js +209 -0
  13. package/dist/src/auth/config.js +38 -0
  14. package/dist/src/auth/credential-store.js +141 -0
  15. package/dist/src/auth/resource-credentials.js +25 -0
  16. package/dist/src/auth/types.js +2 -0
  17. package/dist/src/channel/manager.mjs +5 -5
  18. package/dist/src/cli/handlers/auth.js +125 -0
  19. package/dist/src/cli/handlers/help.js +1 -0
  20. package/dist/src/cli/handlers/index.js +3 -1
  21. package/dist/src/cli/handlers/install.js +42 -4
  22. package/dist/src/cli/handlers/marketplace.js +375 -122
  23. package/dist/src/cli/handlers/resource-versions.js +2 -0
  24. package/dist/src/cli/handlers/sessions.js +23 -5
  25. package/dist/src/cli/handlers/subcommands.js +10 -1
  26. package/dist/src/cli/handlers/use.js +342 -43
  27. package/dist/src/config/gitignore.js +1 -0
  28. package/dist/src/extensions/commands/definitions.js +19 -0
  29. package/dist/src/marketplace/exchange.js +602 -0
  30. package/dist/src/marketplace/provenance-types.js +19 -0
  31. package/dist/src/marketplace/provenance.js +834 -0
  32. package/dist/src/memory/canonical-context.js +342 -0
  33. package/dist/src/memory/context-pack.js +282 -0
  34. package/dist/src/memory/index.js +4 -0
  35. package/dist/src/memory/intake.js +118 -0
  36. package/dist/src/packages/adapters/git.js +79 -29
  37. package/dist/src/packages/package-discovery.js +81 -0
  38. package/dist/src/packages/package-registry.js +2 -0
  39. package/dist/src/packages/registry.js +119 -20
  40. package/dist/src/resources/resolver.js +1 -0
  41. package/dist/src/resources/web-release.d.ts +3 -1
  42. package/dist/src/resources/web-release.js +14 -6
  43. package/dist/src/serve/agentic-sandbox-fleet-client.js +213 -0
  44. package/dist/src/serve/fleet-mission-conductor.js +293 -0
  45. package/dist/src/sessions/index.js +1 -0
  46. package/dist/src/sessions/output-registration.js +338 -0
  47. package/dist/src/sessions/promotion.js +73 -2
  48. package/dist/src/sessions/repository.js +2 -1
  49. package/dist/src/update/notifier.mjs +13 -2
  50. package/package.json +8 -1
  51. package/tools/_resolve-impl.mjs +74 -0
  52. package/tools/agents/deploy-agents.mjs +962 -0
  53. package/tools/agents/providers/base.mjs +2954 -0
  54. package/tools/agents/providers/claude.mjs +711 -0
  55. package/tools/agents/providers/codex.mjs +699 -0
  56. package/tools/agents/providers/copilot.mjs +659 -0
  57. package/tools/agents/providers/cursor.mjs +714 -0
  58. package/tools/agents/providers/factory.mjs +1130 -0
  59. package/tools/agents/providers/hermes.mjs +663 -0
  60. package/tools/agents/providers/hook-capabilities.mjs +85 -0
  61. package/tools/agents/providers/model-role.mjs +56 -0
  62. package/tools/agents/providers/openclaw-translator.mjs +348 -0
  63. package/tools/agents/providers/openclaw.mjs +680 -0
  64. package/tools/agents/providers/opencode.mjs +675 -0
  65. package/tools/agents/providers/openhuman.mjs +292 -0
  66. package/tools/agents/providers/warp.mjs +413 -0
  67. package/tools/agents/providers/windsurf.mjs +748 -0
  68. package/tools/commands/deploy-prompts-codex.mjs +336 -0
  69. package/tools/plugin/package-plugins.mjs +1013 -0
  70. package/tools/skills/deploy-skills-codex.mjs +571 -0
@@ -0,0 +1,338 @@
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, unlinkSync, writeFileSync, } from 'node:fs';
2
+ import { createHash, randomUUID } from 'node:crypto';
3
+ import { dirname, relative, resolve, sep } from 'node:path';
4
+ import { z } from 'zod';
5
+ import { SessionContractError, sha256 } from './contracts.js';
6
+ const DigestSchema = z.string().regex(/^sha256:[0-9a-f]{64}$/);
7
+ const ReferenceSchema = z.string().min(1).max(512).refine(value => !/[\r\n\0]/.test(value), 'references must be single-line inert locators');
8
+ export const OutputSourceReferenceSchema = z.object({
9
+ kind: z.enum(['file', 'url', 'note', 'session', 'artifact', 'context-pack']),
10
+ ref: ReferenceSchema,
11
+ digest: DigestSchema.nullable().default(null),
12
+ span: z.object({
13
+ start: z.number().int().nonnegative(),
14
+ end: z.number().int().positive(),
15
+ quoteDigest: DigestSchema,
16
+ }).strict().nullable().default(null),
17
+ }).strict().superRefine((value, context) => {
18
+ if (value.span && value.span.end <= value.span.start) {
19
+ context.addIssue({ code: z.ZodIssueCode.custom, message: 'source span end must exceed start' });
20
+ }
21
+ });
22
+ export const OutputRegistrationRequestSchema = z.object({
23
+ outputPath: z.string().min(1),
24
+ mediaType: z.string().min(1).max(128),
25
+ contextPack: z.object({
26
+ id: ReferenceSchema,
27
+ digest: DigestSchema,
28
+ sources: z.array(OutputSourceReferenceSchema).min(1).max(256),
29
+ }).strict(),
30
+ supersedes: z.array(ReferenceSchema).max(128).default([]),
31
+ conflictsWith: z.array(ReferenceSchema).max(128).default([]),
32
+ }).strict();
33
+ /**
34
+ * Minimal incremental index sink. Each registration is independently atomic,
35
+ * discoverable, and replay-safe; corpus-wide index builders can consume these
36
+ * bounded records without rescanning output bodies.
37
+ */
38
+ export class FilesystemDerivedOutputIndex {
39
+ root;
40
+ constructor(projectRoot) {
41
+ this.root = resolve(projectRoot, '.aiwg/memory/output-registration/index');
42
+ assertStorageRootInsideProject(projectRoot, this.root);
43
+ }
44
+ register(registration) {
45
+ if (!/^sha256:[0-9a-f]{64}$/.test(registration.registrationId)) {
46
+ throw new Error('invalid output registration identity');
47
+ }
48
+ const filePath = resolve(this.root, `${registration.registrationId.replace(':', '_')}.json`);
49
+ const existing = readJsonIfPresent(filePath);
50
+ if (existing) {
51
+ if (JSON.stringify(existing) !== JSON.stringify(registration)) {
52
+ throw new SessionContractError('IMPORT_CONFLICT', 'derived output index identity already has different content');
53
+ }
54
+ return;
55
+ }
56
+ writeJsonAtomic(filePath, registration);
57
+ }
58
+ registrations() {
59
+ if (!existsSync(this.root))
60
+ return [];
61
+ return readdirSync(this.root)
62
+ .filter(name => /^sha256_[0-9a-f]{64}\.json$/.test(name))
63
+ .sort()
64
+ .map(name => JSON.parse(readFileSync(resolve(this.root, name), 'utf8')));
65
+ }
66
+ }
67
+ function canonicalReference(value) {
68
+ try {
69
+ const parsed = new URL(value);
70
+ if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
71
+ parsed.username = '';
72
+ parsed.password = '';
73
+ parsed.search = '';
74
+ parsed.hash = '';
75
+ return parsed.toString();
76
+ }
77
+ }
78
+ catch {
79
+ // Non-URL references are opaque inert identifiers.
80
+ }
81
+ return value;
82
+ }
83
+ function assertStorageRootInsideProject(projectRoot, storageRoot) {
84
+ const root = realpathSync(projectRoot);
85
+ const candidate = resolve(projectRoot, storageRoot);
86
+ if (candidate !== resolve(projectRoot) && !candidate.startsWith(`${resolve(projectRoot)}${sep}`)) {
87
+ throw new SessionContractError('SOURCE_OUTSIDE_ALLOWED_ROOT', 'memory storage must be inside the project');
88
+ }
89
+ let ancestor = candidate;
90
+ while (!existsSync(ancestor)) {
91
+ const parent = dirname(ancestor);
92
+ if (parent === ancestor)
93
+ break;
94
+ ancestor = parent;
95
+ }
96
+ const actualAncestor = realpathSync(ancestor);
97
+ if (actualAncestor !== root && !actualAncestor.startsWith(`${root}${sep}`)) {
98
+ throw new SessionContractError('SOURCE_OUTSIDE_ALLOWED_ROOT', 'memory storage cannot traverse a link outside the project');
99
+ }
100
+ }
101
+ function assertNoSecretMaterial(value, field) {
102
+ const secretAssignment = /(?:^|[?&;:\s])(?:api[_-]?key|access[_-]?token|token|secret|password|passwd|authorization)\s*[:=]\s*[^\s&;]+/i;
103
+ const privateKey = /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/;
104
+ const providerToken = /(?:^|[^a-z0-9])(?:ghp|github_pat|sk|xox[baprs])_[a-z0-9_-]{16,}/i;
105
+ if (secretAssignment.test(value) || privateKey.test(value) || providerToken.test(value)) {
106
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', `${field} appears to contain secret material; store only a non-secret locator`);
107
+ }
108
+ }
109
+ function canonicalRequest(request) {
110
+ const canonical = {
111
+ ...request,
112
+ contextPack: {
113
+ ...request.contextPack,
114
+ id: canonicalReference(request.contextPack.id),
115
+ sources: request.contextPack.sources.map(source => ({
116
+ ...source,
117
+ ref: canonicalReference(source.ref),
118
+ })),
119
+ },
120
+ supersedes: [...new Set(request.supersedes.map(canonicalReference))].sort(),
121
+ conflictsWith: [...new Set(request.conflictsWith.map(canonicalReference))].sort(),
122
+ };
123
+ assertNoSecretMaterial(canonical.contextPack.id, 'context-pack identity');
124
+ for (const source of canonical.contextPack.sources) {
125
+ assertNoSecretMaterial(source.ref, 'source reference');
126
+ }
127
+ for (const value of [...canonical.supersedes, ...canonical.conflictsWith]) {
128
+ assertNoSecretMaterial(value, 'lifecycle reference');
129
+ }
130
+ return canonical;
131
+ }
132
+ function resolveImmutableOutput(projectRoot, requestedPath) {
133
+ if (requestedPath.includes('\0')) {
134
+ throw new SessionContractError('MALFORMED_SOURCE', 'output path contains a null byte');
135
+ }
136
+ const pathSegments = requestedPath.toLocaleLowerCase().split(/[\\/]+/);
137
+ if (pathSegments.some(segment => segment === '.env'
138
+ || segment === '.ssh'
139
+ || /^(?:credentials?|secrets?|tokens?)(?:\.|$)/.test(segment))) {
140
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'sensitive credential/secret paths cannot be registered as ordinary outputs');
141
+ }
142
+ const root = realpathSync(projectRoot);
143
+ const candidate = realpathSync(resolve(root, requestedPath));
144
+ if (candidate !== root && !candidate.startsWith(`${root}${sep}`)) {
145
+ throw new SessionContractError('SOURCE_OUTSIDE_ALLOWED_ROOT', 'registered output must resolve inside the project');
146
+ }
147
+ return { absolute: candidate, locator: relative(root, candidate).split(sep).join('/') };
148
+ }
149
+ function registrationFor(projectRoot, raw) {
150
+ const request = canonicalRequest(OutputRegistrationRequestSchema.parse(raw));
151
+ const output = resolveImmutableOutput(projectRoot, request.outputPath);
152
+ const content = readFileSync(output.absolute);
153
+ const digest = `sha256:${createHash('sha256').update(content).digest('hex')}`;
154
+ const identity = {
155
+ output: { locator: output.locator, mediaType: request.mediaType, digest, byteLength: content.length },
156
+ contextPack: request.contextPack,
157
+ supersedes: request.supersedes,
158
+ conflictsWith: request.conflictsWith,
159
+ };
160
+ return {
161
+ schemaVersion: 'aiwg.output-registration.v1',
162
+ registrationId: sha256(JSON.stringify(identity)),
163
+ ...identity,
164
+ };
165
+ }
166
+ export class FilesystemOutputRegistrationStore {
167
+ root;
168
+ outboxRoot;
169
+ receiptRoot;
170
+ constructor(projectRoot) {
171
+ this.root = resolve(projectRoot, '.aiwg/memory/output-registration');
172
+ assertStorageRootInsideProject(projectRoot, this.root);
173
+ this.outboxRoot = resolve(this.root, 'outbox');
174
+ this.receiptRoot = resolve(this.root, 'receipts');
175
+ }
176
+ getReceipt(registrationId) {
177
+ return readJsonIfPresent(this.receiptPath(registrationId));
178
+ }
179
+ begin(operationId, registration) {
180
+ const existing = readJsonIfPresent(this.outboxPath(registration.registrationId));
181
+ if (existing)
182
+ return existing;
183
+ const record = {
184
+ schemaVersion: 'aiwg.output-registration-outbox.v1',
185
+ operationId,
186
+ registration,
187
+ state: 'pending',
188
+ attempts: 0,
189
+ lastError: null,
190
+ updatedAt: new Date().toISOString(),
191
+ };
192
+ writeJsonAtomic(this.outboxPath(registration.registrationId), record);
193
+ return record;
194
+ }
195
+ fail(registrationId, message) {
196
+ const path = this.outboxPath(registrationId);
197
+ const record = readJsonIfPresent(path);
198
+ if (!record)
199
+ throw new Error(`missing output-registration outbox record: ${registrationId}`);
200
+ const failed = {
201
+ ...record,
202
+ attempts: record.attempts + 1,
203
+ lastError: message.slice(0, 512),
204
+ updatedAt: new Date().toISOString(),
205
+ };
206
+ writeJsonAtomic(path, failed);
207
+ return failed;
208
+ }
209
+ complete(operationId, registration) {
210
+ const existing = this.getReceipt(registration.registrationId);
211
+ if (existing) {
212
+ unlinkIfPresent(this.outboxPath(registration.registrationId));
213
+ return { ...existing, duplicate: true };
214
+ }
215
+ const receipt = {
216
+ schemaVersion: 'aiwg.output-registration-receipt.v1',
217
+ receiptId: sha256(`${operationId}\0${registration.registrationId}`),
218
+ registrationId: registration.registrationId,
219
+ operationId,
220
+ outputLocator: registration.output.locator,
221
+ outputDigest: registration.output.digest,
222
+ contextPackId: registration.contextPack.id,
223
+ contextPackDigest: registration.contextPack.digest,
224
+ sourceRefs: registration.contextPack.sources.map(source => source.ref),
225
+ registeredAt: new Date().toISOString(),
226
+ duplicate: false,
227
+ };
228
+ writeJsonAtomic(this.receiptPath(registration.registrationId), receipt);
229
+ unlinkIfPresent(this.outboxPath(registration.registrationId));
230
+ return receipt;
231
+ }
232
+ pending() {
233
+ if (!existsSync(this.outboxRoot))
234
+ return [];
235
+ return readdirSync(this.outboxRoot)
236
+ .filter(name => /^sha256_[0-9a-f]{64}\.json$/.test(name))
237
+ .sort()
238
+ .map(name => JSON.parse(readFileSync(resolve(this.outboxRoot, name), 'utf8')));
239
+ }
240
+ safeName(registrationId) {
241
+ if (!/^sha256:[0-9a-f]{64}$/.test(registrationId)) {
242
+ throw new Error('invalid output registration identity');
243
+ }
244
+ return `${registrationId.replace(':', '_')}.json`;
245
+ }
246
+ outboxPath(registrationId) {
247
+ return resolve(this.outboxRoot, this.safeName(registrationId));
248
+ }
249
+ receiptPath(registrationId) {
250
+ return resolve(this.receiptRoot, this.safeName(registrationId));
251
+ }
252
+ }
253
+ export class OutputRegistrationCoordinator {
254
+ store;
255
+ index;
256
+ projectRoot;
257
+ constructor(projectRoot, store, index) {
258
+ this.store = store;
259
+ this.index = index;
260
+ this.projectRoot = resolve(projectRoot);
261
+ }
262
+ preview(request) {
263
+ const registration = registrationFor(this.projectRoot, request);
264
+ const operationId = sha256(JSON.stringify({
265
+ registrationId: registration.registrationId,
266
+ outputDigest: registration.output.digest,
267
+ contextPackDigest: registration.contextPack.digest,
268
+ }));
269
+ return {
270
+ ...registration,
271
+ operationId,
272
+ duplicate: Boolean(this.store.getReceipt(registration.registrationId)),
273
+ confirmationRequired: true,
274
+ };
275
+ }
276
+ async register(input) {
277
+ const preview = this.preview(input.request);
278
+ if (preview.operationId !== input.operationId) {
279
+ throw new SessionContractError('OPERATION_NOT_AUTHORIZED', 'output registration requires confirmation of the exact current preview');
280
+ }
281
+ const existing = this.store.getReceipt(preview.registrationId);
282
+ if (existing)
283
+ return { ...existing, duplicate: true };
284
+ const registration = registrationFor(this.projectRoot, input.request);
285
+ if (registration.registrationId !== preview.registrationId
286
+ || registration.output.digest !== preview.output.digest) {
287
+ throw new SessionContractError('IMPORT_CONFLICT', 'output changed after registration preview');
288
+ }
289
+ this.store.begin(input.operationId, registration);
290
+ try {
291
+ await this.index.register(registration);
292
+ }
293
+ catch (error) {
294
+ this.store.fail(registration.registrationId, error instanceof Error ? error.message : String(error));
295
+ throw error;
296
+ }
297
+ return this.store.complete(input.operationId, registration);
298
+ }
299
+ async replayPending() {
300
+ const receipts = [];
301
+ for (const record of this.store.pending()) {
302
+ try {
303
+ await this.index.register(record.registration);
304
+ receipts.push(this.store.complete(record.operationId, record.registration));
305
+ }
306
+ catch (error) {
307
+ this.store.fail(record.registration.registrationId, error instanceof Error ? error.message : String(error));
308
+ }
309
+ }
310
+ return receipts;
311
+ }
312
+ }
313
+ function readJsonIfPresent(filePath) {
314
+ try {
315
+ return JSON.parse(readFileSync(filePath, 'utf8'));
316
+ }
317
+ catch (error) {
318
+ if (error.code === 'ENOENT')
319
+ return null;
320
+ throw error;
321
+ }
322
+ }
323
+ function writeJsonAtomic(filePath, value) {
324
+ mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
325
+ const temporary = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
326
+ writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
327
+ renameSync(temporary, filePath);
328
+ }
329
+ function unlinkIfPresent(filePath) {
330
+ try {
331
+ unlinkSync(filePath);
332
+ }
333
+ catch (error) {
334
+ if (error.code !== 'ENOENT')
335
+ throw error;
336
+ }
337
+ }
338
+ //# sourceMappingURL=output-registration.js.map
@@ -12,7 +12,7 @@ export class MemoryPromotionGateway {
12
12
  throw new SessionContractError('MALFORMED_SOURCE', 'candidate version does not exist');
13
13
  }
14
14
  const existing = this.store.getPromotionReceipt(input.candidateId, input.version, input.destination.consumer);
15
- if (candidate.reviewState !== 'accepted' && !(candidate.reviewState === 'promoted' && existing)) {
15
+ if (candidate.reviewState !== 'accepted' && candidate.reviewState !== 'promoted') {
16
16
  throw new SessionContractError('OPERATION_NOT_AUTHORIZED', 'promotion requires an accepted exact candidate version');
17
17
  }
18
18
  const security = candidateSecurity(candidate);
@@ -165,7 +165,7 @@ export class FilesystemPromotionDispositionCoordinator {
165
165
  if (!decision) {
166
166
  throw new SessionContractError('OPERATION_NOT_AUTHORIZED', 'every promoted artifact requires an explicit disposition');
167
167
  }
168
- this.authorizedPath(dependent.destinationRef);
168
+ this.authorizedDispositionRef(dependent.destinationRef);
169
169
  return {
170
170
  dependentId: dependent.dependentId,
171
171
  destinationRef: dependent.destinationRef,
@@ -194,6 +194,12 @@ export class FilesystemPromotionDispositionCoordinator {
194
194
  for (const effect of journal.effects) {
195
195
  if (effect.outcome !== 'pending')
196
196
  continue;
197
+ const lineMemory = this.lineMemoryRef(effect.destinationRef);
198
+ if (lineMemory) {
199
+ effect.outcome = this.applyLineMemoryDisposition(lineMemory.metadataPath, lineMemory.handle, purge.operationId, effect);
200
+ this.writeJournal(journalPath, journal);
201
+ continue;
202
+ }
197
203
  const target = this.authorizedPath(effect.destinationRef);
198
204
  const marker = dispositionMarker(purge.operationId, effect);
199
205
  if (effect.action === 'delete') {
@@ -252,6 +258,65 @@ export class FilesystemPromotionDispositionCoordinator {
252
258
  }
253
259
  return target;
254
260
  }
261
+ authorizedDispositionRef(destinationRef) {
262
+ if (this.lineMemoryRef(destinationRef))
263
+ return;
264
+ this.authorizedPath(destinationRef);
265
+ }
266
+ lineMemoryRef(destinationRef) {
267
+ const separator = destinationRef.lastIndexOf('#');
268
+ if (separator < 1)
269
+ return null;
270
+ const metadataRef = destinationRef.slice(0, separator);
271
+ const handle = destinationRef.slice(separator + 1);
272
+ if (!/^lm_[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(handle)) {
273
+ return null;
274
+ }
275
+ return { metadataPath: this.authorizedPath(metadataRef), handle };
276
+ }
277
+ applyLineMemoryDisposition(metadataPath, handle, operationId, effect) {
278
+ if (!existsSync(metadataPath))
279
+ return 'already-applied';
280
+ const metadata = JSON.parse(readFileSync(metadataPath, 'utf8'));
281
+ if (metadata.schemaVersion !== 'aiwg.line-memory.v1' || !metadata.entries) {
282
+ throw new SessionContractError('IMPORT_CONFLICT', 'line-memory metadata schema is invalid');
283
+ }
284
+ const entry = metadata.entries[handle];
285
+ if (!entry)
286
+ return 'already-applied';
287
+ const priorOperation = entry.disposition?.operationId;
288
+ if (priorOperation === operationId)
289
+ return 'already-applied';
290
+ const memoryRef = metadata.store?.memoryPath ?? '.aiwg/memory/line-memory.txt';
291
+ const memoryPath = this.authorizedPath(memoryRef);
292
+ const lines = existsSync(memoryPath)
293
+ ? readFileSync(memoryPath, 'utf8').split(/\r?\n/).filter(Boolean)
294
+ : [];
295
+ const removesActiveFact = ['delete', 'revoke', 'supersede'].includes(effect.action);
296
+ const nextLines = removesActiveFact
297
+ ? removeFirstExact(lines, entry.value)
298
+ : lines;
299
+ if (effect.action === 'delete')
300
+ delete metadata.entries[handle];
301
+ else {
302
+ entry.status = effect.action === 'revoke' ? 'revoked'
303
+ : effect.action === 'supersede' ? 'superseded'
304
+ : effect.action === 'origin_unavailable' ? 'origin-unavailable'
305
+ : 'active';
306
+ entry.updatedAt = new Date().toISOString();
307
+ entry.disposition = {
308
+ operationId,
309
+ action: effect.action,
310
+ effect: effect.effect,
311
+ originAvailable: effect.action !== 'origin_unavailable',
312
+ };
313
+ }
314
+ if (removesActiveFact) {
315
+ this.atomicWrite(memoryPath, nextLines.length ? `${nextLines.join('\n')}\n` : '');
316
+ }
317
+ this.atomicWrite(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`);
318
+ return 'applied';
319
+ }
255
320
  journalPath(operationId) {
256
321
  return resolve(this.journalRoot, `${operationId.replace(':', '-')}.json`);
257
322
  }
@@ -290,6 +355,12 @@ function requireJournalFiles(root) {
290
355
  .sort()
291
356
  .map((name) => resolve(root, name));
292
357
  }
358
+ function removeFirstExact(lines, value) {
359
+ const index = lines.indexOf(value);
360
+ return index < 0
361
+ ? [...lines]
362
+ : lines.filter((_, candidate) => candidate !== index);
363
+ }
293
364
  export function resolveMemoryConsumerManifest(projectRoot, consumer) {
294
365
  const safeConsumer = assertConsumerId(consumer);
295
366
  const candidates = [
@@ -1219,7 +1219,8 @@ export class SessionRepository {
1219
1219
  if (existing)
1220
1220
  return { ...existing, duplicate: true };
1221
1221
  const candidate = this.getCandidate(receipt.candidateId, receipt.candidateVersion);
1222
- if (!candidate || candidate.reviewState !== 'accepted') {
1222
+ if (!candidate
1223
+ || (candidate.reviewState !== 'accepted' && candidate.reviewState !== 'promoted')) {
1223
1224
  throw new SessionContractError('OPERATION_NOT_AUTHORIZED', 'promotion requires an accepted exact candidate version');
1224
1225
  }
1225
1226
  const evidenceIds = [...new Set(candidate.evidence.map((item) => item.eventId))].sort();
@@ -157,6 +157,16 @@ function readCurrentVersion(packageRoot) {
157
157
  }
158
158
  }
159
159
 
160
+ /**
161
+ * Return true when a cached check belongs to the package currently handling
162
+ * the command. A global launcher can dispatch into a newer development
163
+ * checkout, so cache age alone is not sufficient.
164
+ */
165
+ export function cacheMatchesPackage(cache, packageRoot) {
166
+ const currentVersion = readCurrentVersion(packageRoot);
167
+ return !!(currentVersion && cache?.current === currentVersion);
168
+ }
169
+
160
170
  /**
161
171
  * Run the actual update check and write the cache file. Invoked by the
162
172
  * spawned background child via `node src/update/notifier.mjs --check`.
@@ -183,7 +193,7 @@ export function scheduleBackgroundCheck(packageRoot) {
183
193
  if (isDisabled()) return;
184
194
 
185
195
  const cache = readCache();
186
- if (cache?.lastCheckAt) {
196
+ if (cacheMatchesPackage(cache, packageRoot) && cache?.lastCheckAt) {
187
197
  const age = Date.now() - new Date(cache.lastCheckAt).getTime();
188
198
  if (age < CHECK_INTERVAL_MS) return; // recent enough — skip
189
199
  }
@@ -213,9 +223,10 @@ export function scheduleBackgroundCheck(packageRoot) {
213
223
  * is interactive, print a single-line notice to stderr. Never prompts.
214
224
  * Safe to call unconditionally from `bin/aiwg.mjs` — gated by isDisabled().
215
225
  */
216
- export function maybePrintNotice() {
226
+ export function maybePrintNotice(packageRoot) {
217
227
  if (isDisabled()) return;
218
228
  const cache = readCache();
229
+ if (!cacheMatchesPackage(cache, packageRoot)) return;
219
230
  if (!cache?.hasUpdate || !cache.current || !cache.latest) return;
220
231
  // One-line, non-intrusive. Users who want to update run `aiwg update`.
221
232
  const msg = `aiwg: update available ${cache.current} → ${cache.latest} (run: aiwg update)`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwg/cli",
3
- "version": "2026.8.0",
3
+ "version": "2026.8.2",
4
4
  "description": "Lightweight AIWG CLI for signed, versioned web-backed resources.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -25,12 +25,19 @@
25
25
  "files": [
26
26
  "LICENSE",
27
27
  "bin/",
28
+ "agentic/code/providers/",
28
29
  "dist/src/",
29
30
  "!dist/**/*.d.ts",
30
31
  "!dist/**/*.map",
31
32
  "dist/src/api/index.d.ts",
32
33
  "dist/src/resources/index.d.ts",
33
34
  "dist/src/resources/web-release.d.ts",
35
+ "tools/_resolve-impl.mjs",
36
+ "tools/agents/deploy-agents.mjs",
37
+ "tools/agents/providers/",
38
+ "tools/commands/deploy-prompts-codex.mjs",
39
+ "tools/plugin/package-plugins.mjs",
40
+ "tools/skills/deploy-skills-codex.mjs",
34
41
  "README.md"
35
42
  ],
36
43
  "repository": {
@@ -0,0 +1,74 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { dirname, extname, join, resolve } from 'node:path';
3
+ import { fileURLToPath, pathToFileURL } from 'node:url';
4
+
5
+ function findPackageRoot(callerUrl) {
6
+ let dir = dirname(fileURLToPath(callerUrl));
7
+ for (let i = 0; i < 12; i += 1) {
8
+ const pkgPath = join(dir, 'package.json');
9
+ if (existsSync(pkgPath)) {
10
+ try {
11
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
12
+ if (pkg.name === 'aiwg' || pkg.name === '@aiwg/cli') return dir;
13
+ } catch {
14
+ // Keep walking.
15
+ }
16
+ }
17
+
18
+ const parent = dirname(dir);
19
+ if (parent === dir) break;
20
+ dir = parent;
21
+ }
22
+
23
+ throw new Error(`Could not locate AIWG package root from ${callerUrl}`);
24
+ }
25
+
26
+ function candidatesFor(root, relativeFromSrc, base) {
27
+ const primary = resolve(root, base, relativeFromSrc);
28
+ const candidates = [primary];
29
+
30
+ if (base === 'src' && extname(primary) === '.js') {
31
+ candidates.push(primary.slice(0, -3) + '.ts');
32
+ }
33
+
34
+ return candidates;
35
+ }
36
+
37
+ function resolveCandidate(root, relativeFromSrc, base) {
38
+ for (const candidate of candidatesFor(root, relativeFromSrc, base)) {
39
+ if (existsSync(candidate)) return candidate;
40
+ }
41
+ return null;
42
+ }
43
+
44
+ /**
45
+ * Resolve a module that may live under compiled dist/src/ or source src/.
46
+ *
47
+ * Prefers dist/src/ when present. Set AIWG_RESOLVE_IMPL_FROM=dist or src to
48
+ * force a side during CI/package-layout checks.
49
+ */
50
+ export function resolveImpl(callerUrl, relativeFromSrc) {
51
+ const root = findPackageRoot(callerUrl);
52
+ const forced = process.env.AIWG_RESOLVE_IMPL_FROM;
53
+
54
+ const order = forced === 'src'
55
+ ? ['src']
56
+ : forced === 'dist'
57
+ ? ['dist/src']
58
+ : ['dist/src', 'src'];
59
+
60
+ for (const base of order) {
61
+ const candidate = resolveCandidate(root, relativeFromSrc, base);
62
+ if (candidate) return candidate;
63
+ }
64
+
65
+ const attempted = order
66
+ .flatMap((base) => candidatesFor(root, relativeFromSrc, base))
67
+ .join(', ');
68
+ throw new Error(`Could not resolve ${relativeFromSrc}; attempted: ${attempted}`);
69
+ }
70
+
71
+ export async function importImpl(callerUrl, relativeFromSrc) {
72
+ const modulePath = resolveImpl(callerUrl, relativeFromSrc);
73
+ return import(pathToFileURL(modulePath).href);
74
+ }