@aiwg/cli 2026.10.8 → 2026.10.10
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 +197 -3
- package/bin/aiwg.mjs +8 -0
- package/dist/src/artifacts/cli.js +9 -5
- package/dist/src/auth/config.js +3 -8
- package/dist/src/auth/resource-credentials.js +35 -2
- package/dist/src/cli/handlers/diagnose.js +13 -0
- package/dist/src/cli/handlers/mc-admission.js +39 -0
- package/dist/src/cli/handlers/mc.js +2 -31
- package/dist/src/cli/handlers/models-help.js +52 -0
- package/dist/src/cli/handlers/models.js +11 -81
- package/dist/src/cli/handlers/resource-versions.js +8 -5
- package/dist/src/cli/handlers/script-runner.js +13 -1
- package/dist/src/cli/handlers/use-addon-profile.js +72 -0
- package/dist/src/cli/handlers/use.js +209 -233
- package/dist/src/cli/ui.js +1 -0
- package/dist/src/config/aiwg-config.js +38 -184
- package/dist/src/config/cli-help.js +54 -0
- package/dist/src/config/cli.js +5 -69
- package/dist/src/config/config-normalization.js +10 -0
- package/dist/src/config/worker-activation-gate.js +26 -0
- package/dist/src/config/worker-adoption-cli.js +102 -0
- package/dist/src/config/worker-adoption.js +368 -0
- package/dist/src/config/worker-config-digest.js +14 -0
- package/dist/src/config/worker-group-templates.js +150 -0
- package/dist/src/config/worker-guidance-warning.js +28 -0
- package/dist/src/config/worker-owner-lock.js +152 -0
- package/dist/src/config/worker-parallelism.js +154 -0
- package/dist/src/config/worker-write.js +75 -0
- package/dist/src/decision/telemetry/exporter.js +2 -1
- package/dist/src/decision/telemetry/index.js +1 -0
- package/dist/src/decision/telemetry/mapping.js +43 -0
- package/dist/src/decision/telemetry/otlp-http.js +5 -2
- package/dist/src/decision/telemetry/redaction.js +6 -1
- package/dist/src/decision/telemetry/types.js +3 -0
- package/dist/src/decision/telemetry/worker.js +69 -0
- package/dist/src/lint/rule-activation.js +15 -7
- package/dist/src/lint/runner.js +2 -2
- package/dist/src/mcp/server.mjs +2 -11
- package/dist/src/mcp/stdio-lifecycle.mjs +27 -0
- package/dist/src/mcp/tools/worker-failure-diagnostics.mjs +9 -0
- package/dist/src/mcp/tools/worker-invoke.mjs +6 -6
- package/dist/src/mission-protocol/codecs.js +8 -38
- package/dist/src/mission-protocol/worker-contract-diagnostics.js +62 -0
- package/dist/src/mission-protocol/worker-invocation-types.js +2 -0
- package/dist/src/models/conductor-data-policy.d.mts +8 -0
- package/dist/src/models/conductor-data-policy.mjs +190 -0
- package/dist/src/models/config-loader.js +19 -3
- package/dist/src/models/config-writer.js +27 -0
- package/dist/src/models/model-capabilities.v1.json +197 -3
- package/dist/src/models/provider-policy.js +7 -155
- package/dist/src/models/resolver.js +2 -293
- package/dist/src/models/worker-artifact-binding.mjs +12 -0
- package/dist/src/models/worker-direct-readiness-policy.js +155 -0
- package/dist/src/models/worker-direct-readiness.js +9 -218
- package/dist/src/models/worker-health-readiness.js +105 -26
- package/dist/src/models/worker-installation.mjs +12 -14
- package/dist/src/models/worker-member-references.js +64 -0
- package/dist/src/models/worker-plan.js +317 -0
- package/dist/src/models/worker-selection-policy.js +217 -0
- package/dist/src/models/wrapper-deployment.js +1 -1
- package/dist/src/resources/web-release.js +76 -65
- package/dist/src/serve/shared-host-admission-checks.js +116 -0
- package/dist/src/serve/shared-host-scheduler.js +25 -107
- package/dist/src/serve/shared-host-starvation.js +73 -0
- package/dist/src/serve/worker-artifacts.d.mts +4 -0
- package/dist/src/serve/worker-artifacts.mjs +56 -0
- package/dist/src/serve/worker-command-request.js +68 -0
- package/dist/src/serve/worker-invocation-paths.js +144 -0
- package/dist/src/serve/worker-invocation-preparation.js +169 -0
- package/dist/src/serve/worker-invocation.js +24 -291
- package/dist/src/serve/worker-stdin-frame.d.mts +2 -2
- package/dist/src/serve/worker-stdin-frame.mjs +6 -6
- package/dist/src/smiths/context-pipeline/parallelism-section.js +19 -7
- package/dist/src/storage/backends/fortemi.js +33 -29
- package/dist/src/storage/backends/logseq.js +16 -3
- package/dist/src/storage/backends/postgres.js +74 -33
- package/dist/src/storage/backends/postgrest.js +37 -13
- package/dist/src/storage/config.js +6 -1
- package/dist/src/uhp/config.js +4 -0
- package/package.json +9 -1
- package/schemas/mission-v1.schema.json +20 -19
- package/schemas/models/model-config-compatibility.v1.schema.json +16 -0
- package/tools/agents/providers/claude-post-deploy.mjs +35 -0
- package/tools/agents/providers/claude.mjs +3 -30
- package/tools/agents/providers/codex-post-deploy.mjs +13 -0
- package/tools/agents/providers/codex.mjs +2 -8
- package/tools/agents/providers/conductor-data-guard.mjs +61 -0
- package/tools/agents/providers/rule-delivery.mjs +3 -16
- package/tools/agents/providers/rule-presentation.mjs +38 -0
- package/tools/plugin/package-plugins.mjs +1 -1
- package/tools/ralph-external/lib/claude-adapter.mjs +2 -2
- package/tools/ralph-external/lib/codex-adapter.mjs +3 -3
- package/tools/ralph-external/lib/managed-worker-attempt.mjs +86 -0
- package/tools/ralph-external/lib/provider-adapter.mjs +11 -260
- package/tools/ralph-external/lib/session-attempt.mjs +85 -0
- package/tools/ralph-external/lib/strict-worker-json.mjs +190 -0
- package/tools/ralph-external/lib/worker-artifacts.mjs +209 -0
- package/tools/ralph-external/lib/worker-final.mjs +143 -0
- package/tools/ralph-external/lib/worker-inputs.mjs +55 -0
- package/tools/ralph-external/lib/worker-isolation.mjs +2 -44
- package/tools/ralph-external/lib/worker-launch-cli.mjs +47 -0
- package/tools/ralph-external/lib/worker-native-sandbox.mjs +83 -0
- package/tools/ralph-external/session-launcher.mjs +73 -217
- package/vscode-extension/schemas/aiwg.config.v1.json +161 -2
|
@@ -32,20 +32,26 @@
|
|
|
32
32
|
* @issue #958
|
|
33
33
|
*/
|
|
34
34
|
import { existsSync } from 'fs';
|
|
35
|
+
import { resolveSensitiveInput } from '../../auth/sensitive-input.js';
|
|
36
|
+
import { credentialReferenceInput } from '../../mcp/credentials.mjs';
|
|
35
37
|
import { mkdir, readFile, readdir, rm, stat, writeFile } from 'fs/promises';
|
|
36
38
|
import { dirname, join, relative, resolve, sep } from 'path';
|
|
37
39
|
const LOGSEQ_CONFIG_DIR = 'logseq';
|
|
38
40
|
const DEFAULT_API_URL = 'http://127.0.0.1:12315/api';
|
|
39
41
|
export class LogseqAdapter {
|
|
42
|
+
sensitiveInput;
|
|
40
43
|
/** Absolute path to the Logseq graph root. */
|
|
41
44
|
graph;
|
|
42
45
|
apiUrl;
|
|
43
46
|
useApi;
|
|
47
|
+
apiTokenEnv;
|
|
44
48
|
apiWarned = false;
|
|
45
|
-
constructor(config) {
|
|
49
|
+
constructor(config, sensitiveInput = {}) {
|
|
50
|
+
this.sensitiveInput = sensitiveInput;
|
|
46
51
|
this.graph = resolve(expandHome(config.graph));
|
|
47
52
|
this.apiUrl = config.apiUrl ?? DEFAULT_API_URL;
|
|
48
53
|
this.useApi = config.useApi ?? true;
|
|
54
|
+
this.apiTokenEnv = config.apiTokenEnv;
|
|
49
55
|
}
|
|
50
56
|
async init() {
|
|
51
57
|
if (!existsSync(this.graph)) {
|
|
@@ -61,7 +67,14 @@ export class LogseqAdapter {
|
|
|
61
67
|
async probeApi() {
|
|
62
68
|
if (this.apiWarned)
|
|
63
69
|
return;
|
|
64
|
-
const
|
|
70
|
+
const input = credentialReferenceInput(this.apiTokenEnv ?? 'LOGSEQ_API_TOKEN', 'Logseq apiTokenEnv');
|
|
71
|
+
// Keep the optional legacy environment path, including its missing-token warning.
|
|
72
|
+
const token = input.env
|
|
73
|
+
? (this.sensitiveInput.environment ?? process.env)[input.env]
|
|
74
|
+
: await resolveSensitiveInput(input, this.sensitiveInput);
|
|
75
|
+
if (!token && this.apiTokenEnv !== undefined) {
|
|
76
|
+
throw new Error('storage(logseq): apiTokenEnv is not set; use ENV_NAME, file:ABSOLUTE_PATH or store:NAME.');
|
|
77
|
+
}
|
|
65
78
|
if (!token) {
|
|
66
79
|
this.apiWarned = true;
|
|
67
80
|
process.stderr.write(`storage(logseq): API requested (useApi: true) but LOGSEQ_API_TOKEN is not set.\n` +
|
|
@@ -89,7 +102,7 @@ export class LogseqAdapter {
|
|
|
89
102
|
}
|
|
90
103
|
catch {
|
|
91
104
|
this.apiWarned = true;
|
|
92
|
-
process.stderr.write(`storage(logseq): API server
|
|
105
|
+
process.stderr.write(`storage(logseq): API server is not reachable.\n` +
|
|
93
106
|
` AIWG will continue with direct file writes against the graph.\n` +
|
|
94
107
|
` Start Logseq with the API server enabled, or set "useApi": false to suppress.\n`);
|
|
95
108
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { resolveSensitiveInput } from '../../auth/sensitive-input.js';
|
|
2
|
+
import { credentialReferenceInput } from '../../mcp/credentials.mjs';
|
|
1
3
|
import { createHash } from 'node:crypto';
|
|
2
4
|
import { loadFeaturePackage } from '../../features/runtime.js';
|
|
3
5
|
import { STORAGE_BACKEND_CONTRACT, } from '../backend-contract.js';
|
|
@@ -27,6 +29,7 @@ export class PostgresBackendError extends Error {
|
|
|
27
29
|
*/
|
|
28
30
|
export class PostgresStorageBackend {
|
|
29
31
|
options;
|
|
32
|
+
sensitiveInput;
|
|
30
33
|
descriptor = {
|
|
31
34
|
contract: STORAGE_BACKEND_CONTRACT,
|
|
32
35
|
backend: 'postgres-direct',
|
|
@@ -50,8 +53,9 @@ export class PostgresStorageBackend {
|
|
|
50
53
|
statementTimeoutMs;
|
|
51
54
|
lockTimeoutMs;
|
|
52
55
|
idleTransactionTimeoutMs;
|
|
53
|
-
constructor(options) {
|
|
56
|
+
constructor(options, sensitiveInput = {}) {
|
|
54
57
|
this.options = options;
|
|
58
|
+
this.sensitiveInput = sensitiveInput;
|
|
55
59
|
validateIdentity(options.tenant, 'tenant');
|
|
56
60
|
validateIdentity(options.subsystem, 'subsystem');
|
|
57
61
|
this.pool = options.pool;
|
|
@@ -69,7 +73,7 @@ export class PostgresStorageBackend {
|
|
|
69
73
|
}
|
|
70
74
|
async init() {
|
|
71
75
|
if (!this.pool)
|
|
72
|
-
this.pool = await createPool(this.options);
|
|
76
|
+
this.pool = await createPool(this.options, this.sensitiveInput);
|
|
73
77
|
if (this.options.schemaMode !== 'migrate') {
|
|
74
78
|
const schema = await this.runQuery('SELECT schema_version FROM aiwg_storage_schema WHERE singleton = true');
|
|
75
79
|
if (Number(schema.rows[0]?.schema_version) !== 1) {
|
|
@@ -187,7 +191,7 @@ export class PostgresStorageBackend {
|
|
|
187
191
|
}
|
|
188
192
|
/** Hold an exported PostgreSQL snapshot open while parallel consumers page it. */
|
|
189
193
|
async openSnapshotLease() {
|
|
190
|
-
const exporter = await this.
|
|
194
|
+
const exporter = await this.connect();
|
|
191
195
|
let closed = false;
|
|
192
196
|
try {
|
|
193
197
|
await exporter.query('BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY');
|
|
@@ -208,8 +212,11 @@ export class PostgresStorageBackend {
|
|
|
208
212
|
try {
|
|
209
213
|
await exporter.query('COMMIT');
|
|
210
214
|
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
throw classifyPostgresError(error);
|
|
217
|
+
}
|
|
211
218
|
finally {
|
|
212
|
-
exporter
|
|
219
|
+
releaseClient(exporter, false);
|
|
213
220
|
}
|
|
214
221
|
},
|
|
215
222
|
};
|
|
@@ -219,7 +226,7 @@ export class PostgresStorageBackend {
|
|
|
219
226
|
await exporter.query('ROLLBACK');
|
|
220
227
|
}
|
|
221
228
|
catch { /* preserve original error */ }
|
|
222
|
-
exporter
|
|
229
|
+
releaseClient(exporter, true);
|
|
223
230
|
throw classifyPostgresError(error);
|
|
224
231
|
}
|
|
225
232
|
}
|
|
@@ -228,7 +235,7 @@ export class PostgresStorageBackend {
|
|
|
228
235
|
throw new PostgresBackendError('AIWG_POSTGRES_SNAPSHOT_INVALID', 'exported snapshot identifier is malformed');
|
|
229
236
|
}
|
|
230
237
|
const boundedLimit = bounded(limit, 1000, 1, 10_000, 'limit');
|
|
231
|
-
const client = await this.
|
|
238
|
+
const client = await this.connect();
|
|
232
239
|
let failed = false;
|
|
233
240
|
try {
|
|
234
241
|
await client.query('BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY');
|
|
@@ -252,7 +259,7 @@ export class PostgresStorageBackend {
|
|
|
252
259
|
throw classifyPostgresError(error);
|
|
253
260
|
}
|
|
254
261
|
finally {
|
|
255
|
-
client
|
|
262
|
+
releaseClient(client, failed);
|
|
256
263
|
}
|
|
257
264
|
}
|
|
258
265
|
async changes(cursor) {
|
|
@@ -322,12 +329,17 @@ export class PostgresStorageBackend {
|
|
|
322
329
|
});
|
|
323
330
|
}
|
|
324
331
|
async close() {
|
|
325
|
-
|
|
326
|
-
|
|
332
|
+
try {
|
|
333
|
+
if (this.ownsPool && this.pool)
|
|
334
|
+
await this.pool.end();
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
throw classifyPostgresError(error);
|
|
338
|
+
}
|
|
327
339
|
this.pool = undefined;
|
|
328
340
|
}
|
|
329
341
|
async withTransaction(operation, isolation = 'SERIALIZABLE') {
|
|
330
|
-
const client = await this.
|
|
342
|
+
const client = await this.connect();
|
|
331
343
|
let failed = false;
|
|
332
344
|
try {
|
|
333
345
|
await client.query(`BEGIN ISOLATION LEVEL ${isolation}`);
|
|
@@ -347,7 +359,7 @@ export class PostgresStorageBackend {
|
|
|
347
359
|
throw classifyPostgresError(error);
|
|
348
360
|
}
|
|
349
361
|
finally {
|
|
350
|
-
client
|
|
362
|
+
releaseClient(client, failed);
|
|
351
363
|
}
|
|
352
364
|
}
|
|
353
365
|
assertMutationIdentity(mutation) {
|
|
@@ -356,6 +368,14 @@ export class PostgresStorageBackend {
|
|
|
356
368
|
throw new PostgresBackendError('AIWG_POSTGRES_IDENTITY_MISMATCH', 'mutation identity is outside this backend tenant/subsystem');
|
|
357
369
|
}
|
|
358
370
|
}
|
|
371
|
+
async connect() {
|
|
372
|
+
try {
|
|
373
|
+
return await this.requiredPool().connect();
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
throw classifyPostgresError(error);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
359
379
|
requiredPool() {
|
|
360
380
|
if (!this.pool)
|
|
361
381
|
throw new PostgresBackendError('AIWG_POSTGRES_NOT_INITIALIZED', 'call init() before using the PostgreSQL backend');
|
|
@@ -370,11 +390,18 @@ export class PostgresStorageBackend {
|
|
|
370
390
|
}
|
|
371
391
|
}
|
|
372
392
|
}
|
|
373
|
-
async function createPool(options) {
|
|
393
|
+
async function createPool(options, sensitiveInput) {
|
|
374
394
|
const envName = options.connectionStringEnv ?? 'AIWG_POSTGRES_URL';
|
|
375
|
-
const
|
|
395
|
+
const input = credentialReferenceInput(envName, 'PostgreSQL connectionStringEnv');
|
|
396
|
+
let connectionString;
|
|
397
|
+
try {
|
|
398
|
+
connectionString = input.env ? (sensitiveInput.environment ?? process.env)[input.env] : await resolveSensitiveInput(input, sensitiveInput);
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
throw new PostgresBackendError('AIWG_POSTGRES_CREDENTIAL_UNAVAILABLE', error.message);
|
|
402
|
+
}
|
|
376
403
|
if (!connectionString) {
|
|
377
|
-
throw new PostgresBackendError('AIWG_POSTGRES_CREDENTIAL_UNAVAILABLE',
|
|
404
|
+
throw new PostgresBackendError('AIWG_POSTGRES_CREDENTIAL_UNAVAILABLE', 'PostgreSQL connectionStringEnv is not set; use ENV_NAME (default AIWG_POSTGRES_URL), file:ABSOLUTE_PATH or store:NAME.');
|
|
378
405
|
}
|
|
379
406
|
if (options.ssl === undefined) {
|
|
380
407
|
throw new PostgresBackendError('AIWG_POSTGRES_TLS_REQUIRED', 'PostgreSQL remote connections require explicit ssl=require or ssl=verify-full');
|
|
@@ -382,24 +409,29 @@ async function createPool(options) {
|
|
|
382
409
|
if (options.ssl === 'disable' && !isLoopbackPostgresUrl(connectionString)) {
|
|
383
410
|
throw new PostgresBackendError('AIWG_POSTGRES_TLS_REQUIRED', 'ssl=disable is limited to explicit loopback development endpoints');
|
|
384
411
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
412
|
+
try {
|
|
413
|
+
const pg = await loadFeaturePackage('pg');
|
|
414
|
+
const defaultExport = pg.default;
|
|
415
|
+
const Pool = (pg.Pool ?? defaultExport?.Pool);
|
|
416
|
+
if (!Pool)
|
|
417
|
+
throw new PostgresBackendError('AIWG_POSTGRES_DRIVER_INVALID', 'optional pg package does not export Pool');
|
|
418
|
+
const pool = new Pool({
|
|
419
|
+
connectionString,
|
|
420
|
+
max: bounded(options.maxConnections, DEFAULT_POOL_MAX, 1, 100, 'maxConnections'),
|
|
421
|
+
connectionTimeoutMillis: bounded(options.connectionTimeoutMs, DEFAULT_TIMEOUT_MS, 1, 600_000, 'connectionTimeoutMs'),
|
|
422
|
+
statement_timeout: bounded(options.statementTimeoutMs, DEFAULT_TIMEOUT_MS, 1, 600_000, 'statementTimeoutMs'),
|
|
423
|
+
application_name: options.applicationName ?? 'aiwg',
|
|
424
|
+
ssl: options.ssl === 'disable' ? false : { rejectUnauthorized: options.ssl === 'verify-full' },
|
|
425
|
+
});
|
|
426
|
+
// pg emits idle-client failures at pool scope. Registering a listener keeps
|
|
427
|
+
// server restarts from becoming uncaught process exceptions; failed clients
|
|
428
|
+
// are evicted by pg and the next backend operation reconnects normally.
|
|
429
|
+
pool.on?.('error', () => undefined);
|
|
430
|
+
return pool;
|
|
431
|
+
}
|
|
432
|
+
catch (error) {
|
|
433
|
+
throw classifyPostgresError(error);
|
|
434
|
+
}
|
|
403
435
|
}
|
|
404
436
|
function isLoopbackPostgresUrl(connectionString) {
|
|
405
437
|
try {
|
|
@@ -468,6 +500,15 @@ function classifyPostgresError(error) {
|
|
|
468
500
|
if (['40001', '40P01', '55P03', '57014', '57P01', '57P02', '57P03', '08000', '08003', '08006', '08001'].includes(code)) {
|
|
469
501
|
return new PostgresBackendError('AIWG_POSTGRES_RETRYABLE', `retryable PostgreSQL failure (${code})`, true);
|
|
470
502
|
}
|
|
471
|
-
|
|
503
|
+
// Drivers may echo the entire connection string, passwords or server detail.
|
|
504
|
+
return new PostgresBackendError('AIWG_POSTGRES_OPERATION_FAILED', 'PostgreSQL operation failed; check connectivity, credentials and server configuration.');
|
|
505
|
+
}
|
|
506
|
+
function releaseClient(client, failed) {
|
|
507
|
+
try {
|
|
508
|
+
client.release(failed);
|
|
509
|
+
}
|
|
510
|
+
catch (error) {
|
|
511
|
+
throw classifyPostgresError(error);
|
|
512
|
+
}
|
|
472
513
|
}
|
|
473
514
|
//# sourceMappingURL=postgres.js.map
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { resolveSensitiveInput } from '../../auth/sensitive-input.js';
|
|
2
|
+
import { credentialReferenceInput } from '../../mcp/credentials.mjs';
|
|
1
3
|
import { STORAGE_BACKEND_CONTRACT } from '../backend-contract.js';
|
|
2
4
|
import { postgresBatchId, postgresPayloadDigest } from './postgres.js';
|
|
3
5
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
@@ -18,6 +20,7 @@ export class PostgrestBackendError extends Error {
|
|
|
18
20
|
/** PostgreSQL accessMode=postgrest; not a separate storage engine. */
|
|
19
21
|
export class PostgrestStorageBackend {
|
|
20
22
|
options;
|
|
23
|
+
sensitiveInput;
|
|
21
24
|
descriptor = {
|
|
22
25
|
contract: STORAGE_BACKEND_CONTRACT,
|
|
23
26
|
backend: 'postgres-postgrest',
|
|
@@ -42,8 +45,9 @@ export class PostgrestStorageBackend {
|
|
|
42
45
|
maxResponseBytes;
|
|
43
46
|
maxBatchSize;
|
|
44
47
|
maxPageSize;
|
|
45
|
-
constructor(options) {
|
|
48
|
+
constructor(options, sensitiveInput = {}) {
|
|
46
49
|
this.options = options;
|
|
50
|
+
this.sensitiveInput = sensitiveInput;
|
|
47
51
|
this.baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
48
52
|
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
49
53
|
if (!this.fetchImpl)
|
|
@@ -185,7 +189,7 @@ export class PostgrestStorageBackend {
|
|
|
185
189
|
headers['Content-Type'] = options.contentType ?? 'application/json';
|
|
186
190
|
if (options.prefer)
|
|
187
191
|
headers.Prefer = options.prefer;
|
|
188
|
-
const authorization = this.authorization();
|
|
192
|
+
const authorization = await this.authorization();
|
|
189
193
|
if (authorization)
|
|
190
194
|
headers.Authorization = authorization;
|
|
191
195
|
let response;
|
|
@@ -195,8 +199,8 @@ export class PostgrestStorageBackend {
|
|
|
195
199
|
signal: AbortSignal.timeout(this.timeoutMs),
|
|
196
200
|
});
|
|
197
201
|
}
|
|
198
|
-
catch
|
|
199
|
-
throw new PostgrestBackendError('AIWG_POSTGREST_TRANSPORT_FAILED',
|
|
202
|
+
catch {
|
|
203
|
+
throw new PostgrestBackendError('AIWG_POSTGREST_TRANSPORT_FAILED', 'PostgREST transport failed; check connectivity and authorization.', true);
|
|
200
204
|
}
|
|
201
205
|
if (response.status === 204)
|
|
202
206
|
return undefined;
|
|
@@ -204,15 +208,23 @@ export class PostgrestStorageBackend {
|
|
|
204
208
|
if (declaredLength > this.maxResponseBytes) {
|
|
205
209
|
throw new PostgrestBackendError('AIWG_POSTGREST_RESPONSE_TOO_LARGE', `response exceeds ${this.maxResponseBytes} byte ceiling`);
|
|
206
210
|
}
|
|
207
|
-
|
|
211
|
+
let responseBody;
|
|
212
|
+
try {
|
|
213
|
+
responseBody = await response.text();
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
throw new PostgrestBackendError('AIWG_POSTGREST_TRANSPORT_FAILED', 'PostgREST response could not be read.', true);
|
|
217
|
+
}
|
|
208
218
|
if (Buffer.byteLength(responseBody) > this.maxResponseBytes) {
|
|
209
219
|
throw new PostgrestBackendError('AIWG_POSTGREST_RESPONSE_TOO_LARGE', `response exceeds ${this.maxResponseBytes} byte ceiling`);
|
|
210
220
|
}
|
|
211
221
|
if (!response.ok) {
|
|
212
|
-
const
|
|
222
|
+
const secrets = authorization
|
|
223
|
+
? [authorization, authorization.trim(), authorization.trim().replace(/^\S+\s+/, '')].filter(Boolean) : [];
|
|
224
|
+
const databaseError = parseDatabaseError(responseBody, secrets);
|
|
213
225
|
const retryable = response.status === 408 || response.status === 425 || response.status === 429 ||
|
|
214
226
|
response.status >= 500 || isRetryableDatabaseCode(databaseError.databaseCode);
|
|
215
|
-
const exposedCode = databaseError.message
|
|
227
|
+
const exposedCode = /^AIWG_[A-Z0-9_]+$/.test(databaseError.message ?? '')
|
|
216
228
|
? databaseError.message
|
|
217
229
|
: retryable ? 'AIWG_POSTGREST_RETRYABLE' : 'AIWG_POSTGREST_REQUEST_FAILED';
|
|
218
230
|
throw new PostgrestBackendError(exposedCode, `PostgREST request failed with HTTP ${response.status}${databaseError.databaseCode ? ` (database ${databaseError.databaseCode})` : ''}`, retryable, parseRetryAfter(response.headers.get('retry-after')));
|
|
@@ -224,12 +236,19 @@ export class PostgrestStorageBackend {
|
|
|
224
236
|
throw new PostgrestBackendError('AIWG_POSTGREST_RESPONSE_INVALID', 'PostgREST returned invalid JSON');
|
|
225
237
|
}
|
|
226
238
|
}
|
|
227
|
-
authorization() {
|
|
239
|
+
async authorization() {
|
|
228
240
|
if (!this.options.authorizationEnv)
|
|
229
241
|
return undefined;
|
|
230
|
-
const
|
|
242
|
+
const input = credentialReferenceInput(this.options.authorizationEnv, 'PostgREST authorizationEnv');
|
|
243
|
+
let value;
|
|
244
|
+
try {
|
|
245
|
+
value = input.env ? (this.sensitiveInput.environment ?? process.env)[input.env] : await resolveSensitiveInput(input, this.sensitiveInput);
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
throw new PostgrestBackendError('AIWG_POSTGREST_CREDENTIAL_UNAVAILABLE', error.message);
|
|
249
|
+
}
|
|
231
250
|
if (!value)
|
|
232
|
-
throw new PostgrestBackendError('AIWG_POSTGREST_CREDENTIAL_UNAVAILABLE',
|
|
251
|
+
throw new PostgrestBackendError('AIWG_POSTGREST_CREDENTIAL_UNAVAILABLE', 'PostgREST authorizationEnv is not set; use ENV_NAME, file:ABSOLUTE_PATH or store:NAME.');
|
|
233
252
|
return value;
|
|
234
253
|
}
|
|
235
254
|
assertMutationIdentity(mutation) {
|
|
@@ -294,12 +313,17 @@ function parseCsv(input) {
|
|
|
294
313
|
}
|
|
295
314
|
return { header, rows: output };
|
|
296
315
|
}
|
|
297
|
-
function parseDatabaseError(body) {
|
|
316
|
+
function parseDatabaseError(body, secrets) {
|
|
298
317
|
try {
|
|
299
318
|
const parsed = JSON.parse(body);
|
|
319
|
+
// Decode JSON escapes before redaction: encoded echoes must not become codes.
|
|
320
|
+
const redact = (value) => typeof value === 'string'
|
|
321
|
+
? secrets.reduce((text, secret) => text.split(secret).join('[REDACTED]'), value) : undefined;
|
|
322
|
+
const databaseCode = redact(parsed.code);
|
|
323
|
+
const message = redact(parsed.message);
|
|
300
324
|
return {
|
|
301
|
-
...(
|
|
302
|
-
...(
|
|
325
|
+
...(databaseCode && /^[A-Z0-9]{5}$/.test(databaseCode) ? { databaseCode } : {}),
|
|
326
|
+
...(message ? { message } : {}),
|
|
303
327
|
};
|
|
304
328
|
}
|
|
305
329
|
catch {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* @issue #953
|
|
15
15
|
*/
|
|
16
16
|
import { existsSync } from 'fs';
|
|
17
|
+
import { credentialReferenceInput } from '../mcp/credentials.mjs';
|
|
17
18
|
import { readFile } from 'fs/promises';
|
|
18
19
|
import { resolve, isAbsolute, join } from 'path';
|
|
19
20
|
import { homedir } from 'os';
|
|
@@ -171,8 +172,12 @@ function validateBackendConfig(raw, source) {
|
|
|
171
172
|
requireString(obj, 'vault', source);
|
|
172
173
|
return obj;
|
|
173
174
|
case 'logseq':
|
|
174
|
-
rejectUnknownKeys(obj, ['type', 'graph', 'apiUrl', 'useApi'], source, 'backend');
|
|
175
|
+
rejectUnknownKeys(obj, ['type', 'graph', 'apiUrl', 'apiTokenEnv', 'useApi'], source, 'backend');
|
|
175
176
|
requireString(obj, 'graph', source);
|
|
177
|
+
if (obj['apiTokenEnv'] !== undefined) {
|
|
178
|
+
requireString(obj, 'apiTokenEnv', source);
|
|
179
|
+
credentialReferenceInput(obj['apiTokenEnv'], 'Logseq apiTokenEnv');
|
|
180
|
+
}
|
|
176
181
|
return obj;
|
|
177
182
|
case 'notion': {
|
|
178
183
|
rejectUnknownKeys(obj, ['type', 'parent', 'externalIdProperty'], source, 'backend');
|
package/dist/src/uhp/config.js
CHANGED
|
@@ -100,6 +100,10 @@ export function validateUhpConfig(config) {
|
|
|
100
100
|
if (!profile.credential || profile.credential.source !== 'env' || !/^[A-Z_][A-Z0-9_]*$/.test(profile.credential.name ?? '')) {
|
|
101
101
|
errors.push(`${where}.credential: must be an env secret reference with an uppercase variable name`);
|
|
102
102
|
}
|
|
103
|
+
if (profile.credential && typeof profile.credential === 'object'
|
|
104
|
+
&& Object.keys(profile.credential).some(key => key !== 'source' && key !== 'name')) {
|
|
105
|
+
errors.push(`${where}.credential: must contain only source and name; inline credentials are forbidden`);
|
|
106
|
+
}
|
|
103
107
|
if (profile.defaultHarness !== undefined && !OBJECT_ID.test(profile.defaultHarness))
|
|
104
108
|
errors.push(`${where}.defaultHarness: malformed UHP harness id`);
|
|
105
109
|
if (profile.defaultModel !== undefined && !MODEL_ID.test(profile.defaultModel))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiwg/cli",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.10",
|
|
4
4
|
"description": "Lightweight AIWG CLI for signed, versioned web-backed resources.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,7 +49,11 @@
|
|
|
49
49
|
"tools/ralph-external/session-launcher.mjs",
|
|
50
50
|
"tools/ralph-external/process-monitor.mjs",
|
|
51
51
|
"tools/ralph-external/lib/session-environment.mjs",
|
|
52
|
+
"tools/ralph-external/lib/worker-inputs.mjs",
|
|
53
|
+
"tools/ralph-external/lib/worker-final.mjs",
|
|
54
|
+
"tools/ralph-external/lib/worker-launch-cli.mjs",
|
|
52
55
|
"tools/ralph-external/lib/worker-isolation.mjs",
|
|
56
|
+
"tools/ralph-external/lib/worker-native-sandbox.mjs",
|
|
53
57
|
"tools/ralph-external/lib/systemd-client.mjs",
|
|
54
58
|
"tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs",
|
|
55
59
|
"tools/ralph-external/lib/launcher-recovery-evidence.mjs",
|
|
@@ -57,7 +61,11 @@
|
|
|
57
61
|
"tools/ralph-external/lib/worker-overflow.mjs",
|
|
58
62
|
"tools/scripts/resource-preflight.mjs",
|
|
59
63
|
"tools/ralph-external/lib/provider-adapter.mjs",
|
|
64
|
+
"tools/ralph-external/lib/strict-worker-json.mjs",
|
|
65
|
+
"tools/ralph-external/lib/session-attempt.mjs",
|
|
66
|
+
"tools/ralph-external/lib/managed-worker-attempt.mjs",
|
|
60
67
|
"tools/ralph-external/lib/worker-health.mjs",
|
|
68
|
+
"tools/ralph-external/lib/worker-artifacts.mjs",
|
|
61
69
|
"tools/ralph-external/lib/claude-adapter.mjs",
|
|
62
70
|
"tools/ralph-external/lib/codex-adapter.mjs",
|
|
63
71
|
"tools/ralph-external/lib/opencode-adapter.mjs",
|
|
@@ -1643,6 +1643,7 @@
|
|
|
1643
1643
|
"type": "object",
|
|
1644
1644
|
"additionalProperties": false,
|
|
1645
1645
|
"properties": {
|
|
1646
|
+
"invocationId": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]{0,51}(?![\\s\\S])" },
|
|
1646
1647
|
"isolation": {
|
|
1647
1648
|
"enum": ["systemd-unit", "native-sandbox"]
|
|
1648
1649
|
},
|
|
@@ -1703,28 +1704,24 @@
|
|
|
1703
1704
|
]
|
|
1704
1705
|
},
|
|
1705
1706
|
"artifacts": {
|
|
1706
|
-
"type": "array",
|
|
1707
|
-
"
|
|
1707
|
+
"type": "array", "maxItems": 16,
|
|
1708
|
+
"contains": { "type": "object", "required": ["location"], "properties": { "location": { "const": "workspace-relative" } } },
|
|
1709
|
+
"minContains": 0, "maxContains": 1,
|
|
1708
1710
|
"items": {
|
|
1709
|
-
"type": "object",
|
|
1710
|
-
"additionalProperties": false,
|
|
1711
|
+
"type": "object", "additionalProperties": false,
|
|
1711
1712
|
"properties": {
|
|
1712
|
-
"name": {
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
},
|
|
1716
|
-
"
|
|
1717
|
-
"type": "string",
|
|
1718
|
-
"maxLength": 1048576
|
|
1719
|
-
},
|
|
1720
|
-
"location": {
|
|
1721
|
-
"const": "run-directory"
|
|
1722
|
-
}
|
|
1713
|
+
"name": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,63}(?![\\s\\S])" },
|
|
1714
|
+
"content": { "type": "string", "maxLength": 1048576 },
|
|
1715
|
+
"location": { "enum": ["run-directory", "workspace-relative"] },
|
|
1716
|
+
"prefix": { "type": "string", "minLength": 1, "maxLength": 256 },
|
|
1717
|
+
"relativePath": { "type": "string", "minLength": 1, "maxLength": 4096 }
|
|
1723
1718
|
},
|
|
1724
|
-
"required": [
|
|
1725
|
-
|
|
1726
|
-
"
|
|
1727
|
-
|
|
1719
|
+
"required": ["name", "content"],
|
|
1720
|
+
"allOf": [{
|
|
1721
|
+
"if": { "required": ["location"], "properties": { "location": { "const": "workspace-relative" } } },
|
|
1722
|
+
"then": { "required": ["prefix", "relativePath"] },
|
|
1723
|
+
"else": { "not": { "anyOf": [{ "required": ["prefix"] }, { "required": ["relativePath"] }] } }
|
|
1724
|
+
}]
|
|
1728
1725
|
}
|
|
1729
1726
|
},
|
|
1730
1727
|
"briefPath": {
|
|
@@ -1871,6 +1868,10 @@
|
|
|
1871
1868
|
"controlEvidence"
|
|
1872
1869
|
],
|
|
1873
1870
|
"allOf": [
|
|
1871
|
+
{
|
|
1872
|
+
"if": { "required": ["artifacts"], "properties": { "artifacts": { "type": "array", "contains": { "type": "object", "required": ["location"], "properties": { "location": { "const": "workspace-relative" } } } } } },
|
|
1873
|
+
"then": { "required": ["invocationId"], "properties": { "transport": { "const": "shell" } } }
|
|
1874
|
+
},
|
|
1874
1875
|
{
|
|
1875
1876
|
"if": {
|
|
1876
1877
|
"properties": {
|
|
@@ -184,6 +184,13 @@
|
|
|
184
184
|
"type": "object",
|
|
185
185
|
"additionalProperties": false,
|
|
186
186
|
"properties": {
|
|
187
|
+
"baseRole": {
|
|
188
|
+
"enum": [
|
|
189
|
+
"efficiency",
|
|
190
|
+
"coding",
|
|
191
|
+
"reasoning"
|
|
192
|
+
]
|
|
193
|
+
},
|
|
187
194
|
"groups": {
|
|
188
195
|
"type": "array",
|
|
189
196
|
"items": {
|
|
@@ -248,6 +255,15 @@
|
|
|
248
255
|
"type": "object",
|
|
249
256
|
"additionalProperties": false,
|
|
250
257
|
"properties": {
|
|
258
|
+
"localOnlyPaths": {
|
|
259
|
+
"type": "array",
|
|
260
|
+
"uniqueItems": true,
|
|
261
|
+
"items": {
|
|
262
|
+
"type": "string",
|
|
263
|
+
"pattern": "^(?!.*(?:^|/)\\.{1,2}(?:/|$))[A-Za-z0-9_.@+,-]+(?:/[A-Za-z0-9_.@+,-]+)*$(?![\\s\\S])"
|
|
264
|
+
},
|
|
265
|
+
"description": "Literal project-relative local-only files or directory subtrees; deployed conductor guards have limited shell coverage."
|
|
266
|
+
},
|
|
251
267
|
"allowedProcessing": {
|
|
252
268
|
"type": "array",
|
|
253
269
|
"items": {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Apply project settings and context after provider file deployment.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { initializeFrameworkWorkspace, ensureDir } from './base.mjs';
|
|
5
|
+
import { deployHookFile } from './claude.mjs';
|
|
6
|
+
import { deployConductorDataGuard } from './conductor-data-guard.mjs';
|
|
7
|
+
|
|
8
|
+
export async function postDeploy(targetDir, opts) {
|
|
9
|
+
initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
|
|
10
|
+
await deployConductorDataGuard(targetDir, 'claude', opts);
|
|
11
|
+
|
|
12
|
+
// Claude-specific post-deployment (settings.json, etc.)
|
|
13
|
+
const claudeDir = path.join(targetDir, '.claude');
|
|
14
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
15
|
+
|
|
16
|
+
// If settings.json doesn't exist, create a minimal one
|
|
17
|
+
if (!fs.existsSync(settingsPath) && !opts.dryRun) {
|
|
18
|
+
ensureDir(claudeDir);
|
|
19
|
+
const settings = {
|
|
20
|
+
version: '1.0',
|
|
21
|
+
created: new Date().toISOString(),
|
|
22
|
+
aiwg: {
|
|
23
|
+
enabled: true,
|
|
24
|
+
mode: opts.mode || 'all'
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
28
|
+
console.log('Created .claude/settings.json');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Hook file architecture: write AIWG.md and wire @AIWG.md into CLAUDE.md
|
|
32
|
+
if (opts.srcRoot && opts.contextFiles !== false) {
|
|
33
|
+
deployHookFile(targetDir, opts);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { postDeploy } from './claude-post-deploy.mjs';
|
|
2
|
+
export { postDeploy };
|
|
1
3
|
import { importImpl } from '../../_resolve-impl.mjs';
|
|
2
4
|
/**
|
|
3
5
|
* Claude Code Provider
|
|
@@ -43,7 +45,6 @@ import {
|
|
|
43
45
|
pruneStaleAiwgSkills,
|
|
44
46
|
computeAllKernelNames,
|
|
45
47
|
parseFrontmatter,
|
|
46
|
-
initializeFrameworkWorkspace,
|
|
47
48
|
filterAgentFiles,
|
|
48
49
|
getAddonAgentFiles,
|
|
49
50
|
getAddonCommandFiles,
|
|
@@ -395,34 +396,6 @@ export function createAgentsMd(target, srcRoot, dryRun) {
|
|
|
395
396
|
// Post-Deployment
|
|
396
397
|
// ============================================================================
|
|
397
398
|
|
|
398
|
-
export async function postDeploy(targetDir, opts) {
|
|
399
|
-
// Initialize framework workspace structure
|
|
400
|
-
initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
|
|
401
|
-
|
|
402
|
-
// Claude-specific post-deployment (settings.json, etc.)
|
|
403
|
-
const claudeDir = path.join(targetDir, '.claude');
|
|
404
|
-
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
405
|
-
|
|
406
|
-
// If settings.json doesn't exist, create a minimal one
|
|
407
|
-
if (!fs.existsSync(settingsPath) && !opts.dryRun) {
|
|
408
|
-
ensureDir(claudeDir);
|
|
409
|
-
const settings = {
|
|
410
|
-
version: '1.0',
|
|
411
|
-
created: new Date().toISOString(),
|
|
412
|
-
aiwg: {
|
|
413
|
-
enabled: true,
|
|
414
|
-
mode: opts.mode || 'all'
|
|
415
|
-
}
|
|
416
|
-
};
|
|
417
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
418
|
-
console.log('Created .claude/settings.json');
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Hook file architecture: write AIWG.md and wire @AIWG.md into CLAUDE.md
|
|
422
|
-
if (opts.srcRoot && opts.contextFiles !== false) {
|
|
423
|
-
deployHookFile(targetDir, opts);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
399
|
|
|
427
400
|
/**
|
|
428
401
|
* Deploy the AIWG.md hook file and add @AIWG.md directive to CLAUDE.md.
|
|
@@ -444,7 +417,7 @@ function interpolateHookTokens(content, counts, targetDir) {
|
|
|
444
417
|
});
|
|
445
418
|
}
|
|
446
419
|
|
|
447
|
-
function deployHookFile(targetDir, opts) {
|
|
420
|
+
export function deployHookFile(targetDir, opts) {
|
|
448
421
|
const { srcRoot, dryRun, counts } = opts;
|
|
449
422
|
const templatePath = path.join(srcRoot, 'agentic', 'code', 'frameworks', 'sdlc-complete', 'templates', 'project', 'AIWG.md');
|
|
450
423
|
const hookDest = path.join(targetDir, 'AIWG.md');
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Apply project settings and context after provider file deployment.
|
|
2
|
+
import { initializeFrameworkWorkspace } from './base.mjs';
|
|
3
|
+
import { createAgentsMd } from './codex.mjs';
|
|
4
|
+
import { deployConductorDataGuard } from './conductor-data-guard.mjs';
|
|
5
|
+
|
|
6
|
+
export async function postDeploy(targetDir, opts) {
|
|
7
|
+
initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
|
|
8
|
+
await deployConductorDataGuard(targetDir, 'codex', opts);
|
|
9
|
+
|
|
10
|
+
if (opts.createAgentsMd) {
|
|
11
|
+
if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
|
|
12
|
+
}
|
|
13
|
+
}
|