@wrenai/genbi 0.0.4 → 0.0.5
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/README.md +64 -2
- package/dist/assets/{echarts-LTFC-ysd.js → echarts-DYM4hjvk.js} +2 -2
- package/dist/assets/index-A_Wrq7Ey.css +1 -0
- package/dist/assets/{index-Bo--QMUN.js → index-DsH129tV.js} +18 -17
- package/dist/index.html +2 -2
- package/dist-server/harness/bundle/schema.d.ts +38 -0
- package/dist-server/harness/bundle/schema.js +18 -10
- package/dist-server/harness/bundle/version.js +1 -1
- package/dist-server/harness/cli-args.d.ts +10 -2
- package/dist-server/harness/cli-args.js +24 -8
- package/dist-server/harness/cli.js +75 -9
- package/dist-server/harness/compile/cache.js +4 -4
- package/dist-server/harness/compile/compose-profile.d.ts +51 -14
- package/dist-server/harness/compile/compose-profile.js +85 -19
- package/dist-server/harness/compile/context-loader.d.ts +56 -0
- package/dist-server/harness/compile/context-loader.js +237 -0
- package/dist-server/harness/compile/errors.d.ts +19 -0
- package/dist-server/harness/compile/errors.js +35 -0
- package/dist-server/harness/compile/index.d.ts +4 -3
- package/dist-server/harness/compile/index.js +4 -3
- package/dist-server/harness/compile/pipeline.d.ts +15 -7
- package/dist-server/harness/compile/pipeline.js +62 -14
- package/dist-server/harness/compile/resolve-binary.d.ts +2 -2
- package/dist-server/harness/compile/resolve-binary.js +3 -3
- package/dist-server/harness/compile/types.d.ts +29 -0
- package/dist-server/harness/compile/warble-identity.d.ts +11 -0
- package/dist-server/harness/compile/warble-identity.js +13 -0
- package/dist-server/harness/components/ai-step.d.ts +4 -0
- package/dist-server/harness/components/ai-step.js +40 -0
- package/dist-server/harness/components/broker.d.ts +60 -0
- package/dist-server/harness/components/broker.js +117 -0
- package/dist-server/harness/components/capability-card.d.ts +98 -0
- package/dist-server/harness/components/capability-card.js +169 -0
- package/dist-server/harness/components/context.d.ts +2 -0
- package/dist-server/harness/components/context.js +30 -0
- package/dist-server/harness/components/display.d.ts +6 -0
- package/dist-server/harness/components/display.js +29 -0
- package/dist-server/harness/components/egress-judge.d.ts +12 -0
- package/dist-server/harness/components/egress-judge.js +27 -0
- package/dist-server/harness/components/egress.d.ts +142 -0
- package/dist-server/harness/components/egress.js +469 -0
- package/dist-server/harness/components/in-process.d.ts +7 -0
- package/dist-server/harness/components/in-process.js +245 -0
- package/dist-server/harness/components/normalize.d.ts +35 -0
- package/dist-server/harness/components/normalize.js +245 -0
- package/dist-server/harness/components/plan.d.ts +31 -0
- package/dist-server/harness/components/plan.js +184 -0
- package/dist-server/harness/components/report.d.ts +207 -0
- package/dist-server/harness/components/report.js +309 -0
- package/dist-server/harness/components/runner.d.ts +193 -0
- package/dist-server/harness/components/runner.js +492 -0
- package/dist-server/harness/components/semantic-guards.d.ts +20 -0
- package/dist-server/harness/components/semantic-guards.js +42 -0
- package/dist-server/harness/components/terminal-json.d.ts +18 -0
- package/dist-server/harness/components/terminal-json.js +82 -0
- package/dist-server/harness/components/usage.d.ts +14 -0
- package/dist-server/harness/components/usage.js +19 -0
- package/dist-server/harness/components/wren-access.d.ts +29 -0
- package/dist-server/harness/components/wren-access.js +233 -0
- package/dist-server/harness/components/zone-dry-run.d.ts +43 -0
- package/dist-server/harness/components/zone-dry-run.js +107 -0
- package/dist-server/harness/components/zone-gate.d.ts +76 -0
- package/dist-server/harness/components/zone-gate.js +144 -0
- package/dist-server/harness/events/index.d.ts +1 -1
- package/dist-server/harness/events/types.d.ts +52 -0
- package/dist-server/harness/index.d.ts +9 -2
- package/dist-server/harness/index.js +5 -1
- package/dist-server/harness/loop/executor.d.ts +1 -0
- package/dist-server/harness/loop/executor.js +37 -15
- package/dist-server/harness/npm-package-resolve.d.ts +2 -0
- package/dist-server/harness/npm-package-resolve.js +16 -4
- package/dist-server/harness/providers/adapters/openai-compatible.d.ts +13 -0
- package/dist-server/harness/providers/adapters/openai-compatible.js +17 -0
- package/dist-server/harness/providers/adapters/openai.d.ts +7 -0
- package/dist-server/harness/providers/adapters/openai.js +6 -0
- package/dist-server/harness/providers/adapters/pi-ai.d.ts +48 -0
- package/dist-server/harness/providers/adapters/pi-ai.js +518 -0
- package/dist-server/harness/providers/binding.d.ts +45 -4
- package/dist-server/harness/providers/binding.js +44 -8
- package/dist-server/harness/providers/index.d.ts +9 -3
- package/dist-server/harness/providers/index.js +10 -3
- package/dist-server/harness/providers/zone.d.ts +96 -0
- package/dist-server/harness/providers/zone.js +112 -0
- package/dist-server/harness/render/answering-query.d.ts +29 -0
- package/dist-server/harness/render/answering-query.js +62 -0
- package/dist-server/harness/render/envelope.d.ts +6 -0
- package/dist-server/harness/render/envelope.js +10 -3
- package/dist-server/harness/route/adapter-spec.d.ts +8 -9
- package/dist-server/harness/route/adapter-spec.js +13 -14
- package/dist-server/harness/route/codex-ask-events.js +2 -1
- package/dist-server/harness/route/codex-ask.d.ts +4 -0
- package/dist-server/harness/route/codex-ask.js +69 -30
- package/dist-server/harness/route/codex-host-query.d.ts +52 -0
- package/dist-server/harness/route/codex-host-query.js +191 -0
- package/dist-server/harness/route/codex-local-manifest.d.ts +9 -1
- package/dist-server/harness/route/codex-local-manifest.js +96 -15
- package/dist-server/harness/route/codex-wren-proxy.d.ts +33 -0
- package/dist-server/harness/route/codex-wren-proxy.js +227 -0
- package/dist-server/harness/route/describe.js +45 -6
- package/dist-server/harness/route/in-process.d.ts +22 -1
- package/dist-server/harness/route/in-process.js +54 -4
- package/dist-server/harness/route/route.js +5 -2
- package/dist-server/harness/route/types.d.ts +30 -1
- package/dist-server/harness/session/run.js +30 -10
- package/dist-server/harness/setup/runner.d.ts +2 -0
- package/dist-server/harness/setup/runner.js +38 -17
- package/dist-server/harness/setup/yaml-scalar.d.ts +23 -0
- package/dist-server/harness/setup/yaml-scalar.js +62 -0
- package/dist-server/server/adopt.js +1 -17
- package/dist-server/server/app.d.ts +1 -0
- package/dist-server/server/app.js +163 -14
- package/dist-server/server/auth-choice.d.ts +13 -1
- package/dist-server/server/auth-choice.js +66 -1
- package/dist-server/server/bin.js +177 -25
- package/dist-server/server/codex-local-grounding.d.ts +18 -0
- package/dist-server/server/codex-local-grounding.js +92 -0
- package/dist-server/server/codex-native-session.d.ts +25 -0
- package/dist-server/server/codex-native-session.js +1 -0
- package/dist-server/server/codex-runtime-composition.d.ts +34 -0
- package/dist-server/server/codex-runtime-composition.js +250 -0
- package/dist-server/server/codex-structured-ask.d.ts +23 -0
- package/dist-server/server/codex-structured-ask.js +102 -0
- package/dist-server/server/conn-config.js +4 -16
- package/dist-server/server/conversation-socket.d.ts +16 -0
- package/dist-server/server/conversation-socket.js +65 -0
- package/dist-server/server/db.d.ts +46 -0
- package/dist-server/server/db.js +88 -9
- package/dist-server/server/env-detect.d.ts +6 -0
- package/dist-server/server/env-detect.js +7 -0
- package/dist-server/server/fold.d.ts +22 -4
- package/dist-server/server/fold.js +37 -5
- package/dist-server/server/harness.d.ts +2 -2
- package/dist-server/server/harness.js +33 -7
- package/dist-server/server/interactive-terminal.d.ts +10 -6
- package/dist-server/server/interactive-terminal.js +33 -8
- package/dist-server/server/managed-wren-runtime.d.ts +116 -0
- package/dist-server/server/managed-wren-runtime.js +670 -0
- package/dist-server/server/native-artifacts.d.ts +23 -7
- package/dist-server/server/native-artifacts.js +43 -12
- package/dist-server/server/native-component-context.d.ts +19 -0
- package/dist-server/server/native-component-context.js +76 -0
- package/dist-server/server/native-component-preparation.d.ts +20 -0
- package/dist-server/server/native-component-preparation.js +129 -0
- package/dist-server/server/native-component-runtime.d.ts +36 -0
- package/dist-server/server/native-component-runtime.js +92 -0
- package/dist-server/server/native-components.d.ts +84 -0
- package/dist-server/server/native-components.js +160 -0
- package/dist-server/server/native-dispatch-process.d.ts +8 -0
- package/dist-server/server/native-dispatch-process.js +65 -0
- package/dist-server/server/native-runtime-spec.d.ts +89 -0
- package/dist-server/server/native-runtime-spec.js +169 -0
- package/dist-server/server/native-sessions.d.ts +172 -19
- package/dist-server/server/native-sessions.js +839 -244
- package/dist-server/server/native-shutdown.d.ts +6 -0
- package/dist-server/server/native-shutdown.js +28 -0
- package/dist-server/server/native-wren-home.d.ts +18 -0
- package/dist-server/server/native-wren-home.js +262 -0
- package/dist-server/server/node-pty-host.d.ts +2 -1
- package/dist-server/server/node-pty-host.js +3 -2
- package/dist-server/server/profile-registry.d.ts +139 -0
- package/dist-server/server/profile-registry.js +620 -0
- package/dist-server/server/runtime-binding.d.ts +4 -1
- package/dist-server/server/runtime-binding.js +50 -2
- package/dist-server/server/runtime-host/claude-component-step.d.ts +25 -0
- package/dist-server/server/runtime-host/claude-component-step.js +186 -0
- package/dist-server/server/runtime-host/codex-app-server.d.ts +75 -0
- package/dist-server/server/runtime-host/codex-app-server.js +239 -0
- package/dist-server/server/runtime-host/codex-compatibility.d.ts +110 -0
- package/dist-server/server/runtime-host/codex-compatibility.js +162 -0
- package/dist-server/server/runtime-host/codex-component-step.d.ts +19 -0
- package/dist-server/server/runtime-host/codex-component-step.js +308 -0
- package/dist-server/server/runtime-host/codex-conversation.d.ts +109 -0
- package/dist-server/server/runtime-host/codex-conversation.js +295 -0
- package/dist-server/server/runtime-host/codex-direct-tools.d.ts +23 -0
- package/dist-server/server/runtime-host/codex-direct-tools.js +59 -0
- package/dist-server/server/runtime-host/codex-events.d.ts +647 -0
- package/dist-server/server/runtime-host/codex-events.js +66 -0
- package/dist-server/server/runtime-host/codex-policy.d.ts +16 -0
- package/dist-server/server/runtime-host/codex-policy.js +151 -0
- package/dist-server/server/runtime-host/codex-process.d.ts +8 -0
- package/dist-server/server/runtime-host/codex-process.js +96 -0
- package/dist-server/server/runtime-host/codex-rpc.d.ts +47 -0
- package/dist-server/server/runtime-host/codex-rpc.js +182 -0
- package/dist-server/server/runtime-host/codex-session.d.ts +74 -0
- package/dist-server/server/runtime-host/codex-session.js +492 -0
- package/dist-server/server/runtime-host/codex-terminal-protocol.d.ts +35 -0
- package/dist-server/server/runtime-host/codex-terminal-protocol.js +243 -0
- package/dist-server/server/runtime-host/codex-terminal.d.ts +16 -0
- package/dist-server/server/runtime-host/codex-terminal.js +120 -0
- package/dist-server/server/runtime-host/index.d.ts +5 -0
- package/dist-server/server/runtime-host/index.js +3 -0
- package/dist-server/server/runtime-host/local.d.ts +29 -0
- package/dist-server/server/runtime-host/local.js +90 -0
- package/dist-server/server/runtime-host/policy.d.ts +8 -0
- package/dist-server/server/runtime-host/policy.js +78 -0
- package/dist-server/server/runtime-host/types.d.ts +52 -0
- package/dist-server/server/runtime-host/types.js +62 -0
- package/dist-server/server/structured-query-evidence.d.ts +7 -0
- package/dist-server/server/structured-query-evidence.js +47 -0
- package/dist-server/server/structured-runtime.d.ts +52 -0
- package/dist-server/server/structured-runtime.js +130 -0
- package/dist-server/server/structured-scope.d.ts +4 -0
- package/dist-server/server/structured-scope.js +12 -0
- package/dist-server/server/turn.d.ts +6 -1
- package/dist-server/server/turn.js +82 -25
- package/dist-server/server/wire-types.d.ts +67 -3
- package/dist-server/server/wren-profiles.js +1 -1
- package/managed-wren/README.md +60 -0
- package/managed-wren/dependency-wheels.json +282 -0
- package/managed-wren/manifest.json +373 -0
- package/managed-wren/release-inputs.json +45 -0
- package/managed-wren/runtime-tree.cjs +115 -0
- package/managed-wren/source-inputs.json +40 -0
- package/package.json +15 -6
- package/profiles/genbi-default/context/binding.yml +17 -7
- package/profiles/genbi-default/context/context.json +146 -0
- package/profiles/genbi-default/ir.golden.json +39 -43
- package/profiles/genbi-default/profile.yml +31 -0
- package/profiles/genbi-enrich-context/context/binding.yml +14 -3
- package/profiles/genbi-enrich-context/context/context.json +146 -0
- package/profiles/genbi-enrich-context/ir.golden.json +4 -5
- package/profiles/genbi-monitor/context/binding.yml +13 -5
- package/profiles/genbi-monitor/context/context.json +146 -0
- package/profiles/genbi-monitor/ir.golden.json +13 -7
- package/profiles/genbi-report/context/binding.yml +19 -0
- package/profiles/genbi-report/context/context.json +223 -0
- package/profiles/genbi-report/ir.golden.json +328 -0
- package/profiles/genbi-report/profile.yml +38 -0
- package/profiles/genbi-setup/ir.golden.json +3 -1
- package/dist/assets/index-Xs4q7pNo.css +0 -1
package/dist-server/server/db.js
CHANGED
|
@@ -153,10 +153,21 @@ CREATE TABLE IF NOT EXISTS native_structured_answers (
|
|
|
153
153
|
envelope_json TEXT NOT NULL,
|
|
154
154
|
digest TEXT NOT NULL,
|
|
155
155
|
created_at TEXT NOT NULL,
|
|
156
|
+
grounded INTEGER NOT NULL DEFAULT 0,
|
|
156
157
|
UNIQUE(native_session_id, idempotency_key)
|
|
157
158
|
);
|
|
158
159
|
CREATE INDEX IF NOT EXISTS native_structured_answers_session_created_at
|
|
159
160
|
ON native_structured_answers(native_session_id, created_at DESC);
|
|
161
|
+
-- Warble profile registry: the shipped profiles plus any the operator added. A row's admission
|
|
162
|
+
-- verdict is recomputed when the profile bytes or the warble binary change; rows are never
|
|
163
|
+
-- deleted by admission, only marked unavailable.
|
|
164
|
+
CREATE TABLE IF NOT EXISTS warble_profiles (
|
|
165
|
+
id TEXT PRIMARY KEY, kind TEXT NOT NULL, role TEXT NOT NULL, source_dir TEXT NOT NULL,
|
|
166
|
+
profile_hash TEXT NOT NULL, warble_identity TEXT NOT NULL,
|
|
167
|
+
admission_status TEXT NOT NULL, admission_reason TEXT, entry_kind TEXT, entry_verb TEXT,
|
|
168
|
+
ir_version TEXT, components_json TEXT NOT NULL DEFAULT '[]', rules_version INTEGER NOT NULL DEFAULT 0,
|
|
169
|
+
created_at TEXT NOT NULL, updated_at TEXT NOT NULL, admitted_at TEXT
|
|
170
|
+
);
|
|
160
171
|
`;
|
|
161
172
|
function boolToInt(value) {
|
|
162
173
|
return value ? 1 : 0;
|
|
@@ -245,6 +256,7 @@ function coerceOrphanedTodoSteps(steps) {
|
|
|
245
256
|
}
|
|
246
257
|
export class Store {
|
|
247
258
|
db;
|
|
259
|
+
closed = false;
|
|
248
260
|
onContextSuccessWrite;
|
|
249
261
|
onEnrichmentTransitionWrite;
|
|
250
262
|
onEnrichmentCreationWrite;
|
|
@@ -362,6 +374,9 @@ export class Store {
|
|
|
362
374
|
this.addColumnIfMissing("artifacts", "native_agent", "TEXT");
|
|
363
375
|
this.addColumnIfMissing("artifacts", "content_digest", "TEXT");
|
|
364
376
|
this.addColumnIfMissing("artifacts", "idempotency_key", "TEXT");
|
|
377
|
+
// Pre-existing retained answers carry no provenance, so they default to
|
|
378
|
+
// ungrounded: a later save by reference to one of them is Unverified.
|
|
379
|
+
this.addColumnIfMissing("native_structured_answers", "grounded", "INTEGER NOT NULL DEFAULT 0");
|
|
365
380
|
this.db.exec("CREATE UNIQUE INDEX IF NOT EXISTS artifacts_native_idempotency_unique ON artifacts(native_session_id, idempotency_key) WHERE native_session_id IS NOT NULL AND idempotency_key IS NOT NULL");
|
|
366
381
|
this.addColumnIfMissing("enrichment_operations", "draft", "TEXT NOT NULL DEFAULT ''");
|
|
367
382
|
this.addColumnIfMissing("enrichment_operations", "change_kind", "TEXT NOT NULL DEFAULT 'knowledge_append'");
|
|
@@ -383,10 +398,14 @@ export class Store {
|
|
|
383
398
|
this.addColumnIfMissing("enrichment_approvals", "evidence_ref", "TEXT NOT NULL DEFAULT ''");
|
|
384
399
|
this.addColumnIfMissing("enrichment_approvals", "nonce", "TEXT NOT NULL DEFAULT ''");
|
|
385
400
|
this.addColumnIfMissing("enrichment_approvals", "expires_at", "TEXT NOT NULL DEFAULT ''");
|
|
401
|
+
this.addColumnIfMissing("native_sessions", "entry_verb", "TEXT");
|
|
402
|
+
this.addColumnIfMissing("native_sessions", "transport", "TEXT");
|
|
386
403
|
this.addColumnIfMissing("native_sessions", "dispatch_profile", "TEXT");
|
|
387
404
|
this.addColumnIfMissing("native_sessions", "dispatch_target", "TEXT");
|
|
388
405
|
this.addColumnIfMissing("native_sessions", "runtime_generation", "INTEGER");
|
|
389
406
|
this.addColumnIfMissing("artifacts", "source_answer_id", "TEXT");
|
|
407
|
+
// A database written before admission carried a rule-set version reads 0 and re-admits.
|
|
408
|
+
this.addColumnIfMissing("warble_profiles", "rules_version", "INTEGER NOT NULL DEFAULT 0");
|
|
390
409
|
this.db.exec("CREATE UNIQUE INDEX IF NOT EXISTS enrichment_approvals_nonce_unique ON enrichment_approvals(nonce) WHERE nonce <> ''");
|
|
391
410
|
}
|
|
392
411
|
addColumnIfMissing(table, column, type) {
|
|
@@ -398,7 +417,10 @@ export class Store {
|
|
|
398
417
|
}
|
|
399
418
|
close() {
|
|
400
419
|
this.db.close();
|
|
420
|
+
this.closed = true;
|
|
401
421
|
}
|
|
422
|
+
/** Lets delayed process-local cleanup avoid touching SQLite after shutdown. */
|
|
423
|
+
isClosed() { return this.closed; }
|
|
402
424
|
// ---------------------------------------------------------------------
|
|
403
425
|
// sessions
|
|
404
426
|
// ---------------------------------------------------------------------
|
|
@@ -415,8 +437,8 @@ export class Store {
|
|
|
415
437
|
// ---------------------------------------------------------------------
|
|
416
438
|
createNativeSession(params) {
|
|
417
439
|
const now = this.now().toISOString();
|
|
418
|
-
this.db.prepare(`INSERT INTO native_sessions (id, purpose, vendor, agent, scope_kind, scope_id, project_identity, binding_generation, project_revision, dispatch_profile, dispatch_target, runtime_generation, status, created_at, updated_at, started_at, ended_at, exit_code, failure)
|
|
419
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'creating', ?, ?, NULL, NULL, NULL, NULL)`).run(params.id, params.purpose, params.vendor, params.agent, params.scopeKind, params.scopeId, params.projectIdentity ?? null, params.bindingGeneration ?? null, params.projectRevision ?? null, params.dispatchProfile ?? null, params.dispatchTarget ?? null, params.runtimeGeneration ?? null, now, now);
|
|
440
|
+
this.db.prepare(`INSERT INTO native_sessions (id, purpose, vendor, agent, entry_verb, scope_kind, scope_id, project_identity, binding_generation, project_revision, dispatch_profile, dispatch_target, runtime_generation, status, created_at, updated_at, started_at, ended_at, exit_code, failure, transport)
|
|
441
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'creating', ?, ?, NULL, NULL, NULL, NULL, ?)`).run(params.id, params.purpose, params.vendor, params.agent, params.entryVerb ?? null, params.scopeKind, params.scopeId, params.projectIdentity ?? null, params.bindingGeneration ?? null, params.projectRevision ?? null, params.dispatchProfile ?? null, params.dispatchTarget ?? null, params.runtimeGeneration ?? null, now, now, params.transport ?? null);
|
|
420
442
|
return this.getNativeSession(params.id);
|
|
421
443
|
}
|
|
422
444
|
/** Stores a sealed provider handle only; callers must never pass plaintext. */
|
|
@@ -478,8 +500,8 @@ export class Store {
|
|
|
478
500
|
}
|
|
479
501
|
const now = this.now().toISOString();
|
|
480
502
|
const child = params.child;
|
|
481
|
-
this.db.prepare(`INSERT INTO native_sessions (id, purpose, vendor, agent, scope_kind, scope_id, project_identity, binding_generation, project_revision, dispatch_profile, dispatch_target, runtime_generation, status, created_at, updated_at, started_at, ended_at, exit_code, failure)
|
|
482
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'creating', ?, ?, NULL, NULL, NULL, NULL)`).run(child.id, child.purpose, child.vendor, child.agent, child.scopeKind, child.scopeId, child.projectIdentity ?? null, child.bindingGeneration ?? null, child.projectRevision ?? null, child.dispatchProfile ?? null, child.dispatchTarget ?? null, child.runtimeGeneration ?? null, now, now);
|
|
503
|
+
this.db.prepare(`INSERT INTO native_sessions (id, purpose, vendor, agent, entry_verb, scope_kind, scope_id, project_identity, binding_generation, project_revision, dispatch_profile, dispatch_target, runtime_generation, status, created_at, updated_at, started_at, ended_at, exit_code, failure)
|
|
504
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'creating', ?, ?, NULL, NULL, NULL, NULL)`).run(child.id, child.purpose, child.vendor, child.agent, child.entryVerb ?? null, child.scopeKind, child.scopeId, child.projectIdentity ?? null, child.bindingGeneration ?? null, child.projectRevision ?? null, child.dispatchProfile ?? null, child.dispatchTarget ?? null, child.runtimeGeneration ?? null, now, now);
|
|
483
505
|
this.db.prepare(`INSERT INTO native_session_resume_actions (session_id, idempotency_key, scope_fingerprint, resumed_session_id, created_at) VALUES (?, ?, ?, ?, ?)`).run(params.sourceSessionId, params.idempotencyKey, params.scopeFingerprint, child.id, now);
|
|
484
506
|
this.db.prepare(`UPDATE native_session_resume_handles SET consumed_at = ? WHERE session_id = ? AND consumed_at IS NULL`).run(now, params.sourceSessionId);
|
|
485
507
|
this.saveNativeSessionResumeHandle(child.id, child.vendor, params.sealedHandle);
|
|
@@ -503,8 +525,8 @@ export class Store {
|
|
|
503
525
|
const createdAt = this.now().toISOString();
|
|
504
526
|
this.db.exec("BEGIN IMMEDIATE");
|
|
505
527
|
try {
|
|
506
|
-
this.db.prepare(`INSERT INTO native_structured_answers (id, native_session_id, idempotency_key, envelope_json, digest, created_at)
|
|
507
|
-
VALUES (?, ?, ?, ?, ?, ?)`).run(params.id, params.nativeSessionId, params.idempotencyKey, params.envelopeJson, params.digest, createdAt);
|
|
528
|
+
this.db.prepare(`INSERT INTO native_structured_answers (id, native_session_id, idempotency_key, envelope_json, digest, created_at, grounded)
|
|
529
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(params.id, params.nativeSessionId, params.idempotencyKey, params.envelopeJson, params.digest, createdAt, boolToInt(params.grounded));
|
|
508
530
|
this.db.prepare(`DELETE FROM native_structured_answers
|
|
509
531
|
WHERE id IN (
|
|
510
532
|
SELECT id FROM native_structured_answers
|
|
@@ -539,6 +561,41 @@ export class Store {
|
|
|
539
561
|
LIMIT 1`).get(nativeSessionId);
|
|
540
562
|
return row ? rowToNativeStructuredAnswer(row) : undefined;
|
|
541
563
|
}
|
|
564
|
+
// ---------------------------------------------------------------------
|
|
565
|
+
// warble profile registry
|
|
566
|
+
// ---------------------------------------------------------------------
|
|
567
|
+
upsertWarbleProfile(row) {
|
|
568
|
+
this.db.prepare(`INSERT INTO warble_profiles (id, kind, role, source_dir, profile_hash, warble_identity, admission_status, admission_reason,
|
|
569
|
+
entry_kind, entry_verb, ir_version, components_json, rules_version, created_at, updated_at, admitted_at)
|
|
570
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
571
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
572
|
+
kind = excluded.kind, role = excluded.role, source_dir = excluded.source_dir,
|
|
573
|
+
profile_hash = excluded.profile_hash, warble_identity = excluded.warble_identity,
|
|
574
|
+
admission_status = excluded.admission_status, admission_reason = excluded.admission_reason,
|
|
575
|
+
entry_kind = excluded.entry_kind, entry_verb = excluded.entry_verb, ir_version = excluded.ir_version,
|
|
576
|
+
components_json = excluded.components_json, rules_version = excluded.rules_version,
|
|
577
|
+
updated_at = excluded.updated_at, admitted_at = excluded.admitted_at`).run(row.id, row.kind, row.role, row.sourceDir, row.profileHash, row.warbleIdentity, row.admissionStatus, row.admissionReason, row.entryKind, row.entryVerb, row.irVersion, row.componentsJson, row.rulesVersion, row.createdAt, row.updatedAt, row.admittedAt);
|
|
578
|
+
return this.getWarbleProfile(row.id);
|
|
579
|
+
}
|
|
580
|
+
getWarbleProfile(id) {
|
|
581
|
+
const row = this.db.prepare(`SELECT * FROM warble_profiles WHERE id = ?`).get(id);
|
|
582
|
+
return row ? rowToWarbleProfile(row) : undefined;
|
|
583
|
+
}
|
|
584
|
+
/** Built-ins first (in registration order), then user profiles by id. */
|
|
585
|
+
listWarbleProfiles() {
|
|
586
|
+
return this.db
|
|
587
|
+
.prepare(`SELECT * FROM warble_profiles ORDER BY CASE kind WHEN 'builtin' THEN 0 ELSE 1 END, rowid ASC`)
|
|
588
|
+
.all()
|
|
589
|
+
.map(rowToWarbleProfile);
|
|
590
|
+
}
|
|
591
|
+
deleteWarbleProfile(id) {
|
|
592
|
+
return this.db.prepare(`DELETE FROM warble_profiles WHERE id = ?`).run(id).changes > 0;
|
|
593
|
+
}
|
|
594
|
+
/** How many native sessions ever ran inside this profile; the registry refuses to remove a referenced one. */
|
|
595
|
+
countNativeSessionsByDispatchProfile(profileId) {
|
|
596
|
+
const row = this.db.prepare(`SELECT COUNT(*) AS n FROM native_sessions WHERE dispatch_profile = ?`).get(profileId);
|
|
597
|
+
return row ? num(row, "n") : 0;
|
|
598
|
+
}
|
|
542
599
|
listNativeSessions() {
|
|
543
600
|
return this.db.prepare(`SELECT * FROM native_sessions ORDER BY updated_at DESC, rowid DESC`).all().map(rowToNativeSession);
|
|
544
601
|
}
|
|
@@ -853,7 +910,7 @@ export class Store {
|
|
|
853
910
|
const createdAt = new Date().toISOString();
|
|
854
911
|
const row = {
|
|
855
912
|
id: params.id, sessionId: params.sessionId, name: params.name, kind: "dashboard", location: params.location,
|
|
856
|
-
verified:
|
|
913
|
+
verified: params.verified, createdAt, savedAt: createdAt, nativeSessionId: params.nativeSessionId,
|
|
857
914
|
projectIdentity: params.projectIdentity, bindingGeneration: params.bindingGeneration,
|
|
858
915
|
projectRevision: params.projectRevision, nativeVendor: params.vendor, nativeAgent: params.agent,
|
|
859
916
|
contentDigest: params.digest, idempotencyKey: params.idempotencyKey,
|
|
@@ -865,7 +922,7 @@ export class Store {
|
|
|
865
922
|
native_session_id, project_identity, binding_generation, project_revision,
|
|
866
923
|
native_vendor, native_agent, content_digest, idempotency_key, source_answer_id
|
|
867
924
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
868
|
-
.run(row.id, row.sessionId, row.name, row.kind, row.location,
|
|
925
|
+
.run(row.id, row.sessionId, row.name, row.kind, row.location, boolToInt(row.verified), row.createdAt, row.savedAt, row.nativeSessionId, row.projectIdentity, row.bindingGeneration, row.projectRevision, row.nativeVendor, row.nativeAgent, row.contentDigest, row.idempotencyKey, row.sourceAnswerId);
|
|
869
926
|
return { row, created: true };
|
|
870
927
|
}
|
|
871
928
|
catch (error) {
|
|
@@ -1724,8 +1781,9 @@ function rowToSession(row) {
|
|
|
1724
1781
|
}
|
|
1725
1782
|
function rowToNativeSession(row) {
|
|
1726
1783
|
return {
|
|
1784
|
+
...(row["transport"] === "conversation" ? { transport: "conversation" } : {}),
|
|
1727
1785
|
id: str(row, "id"), purpose: str(row, "purpose"),
|
|
1728
|
-
vendor: str(row, "vendor"), agent: str(row, "agent"),
|
|
1786
|
+
vendor: str(row, "vendor"), agent: str(row, "agent"), entryVerb: strOrNull(row, "entry_verb"),
|
|
1729
1787
|
scopeKind: str(row, "scope_kind"), scopeId: str(row, "scope_id"),
|
|
1730
1788
|
projectIdentity: strOrNull(row, "project_identity"),
|
|
1731
1789
|
bindingGeneration: row["binding_generation"] === null ? null : num(row, "binding_generation"),
|
|
@@ -1739,6 +1797,26 @@ function rowToNativeSession(row) {
|
|
|
1739
1797
|
exitCode: row["exit_code"] === null ? null : num(row, "exit_code"), failure: strOrNull(row, "failure"),
|
|
1740
1798
|
};
|
|
1741
1799
|
}
|
|
1800
|
+
function rowToWarbleProfile(row) {
|
|
1801
|
+
return {
|
|
1802
|
+
id: str(row, "id"),
|
|
1803
|
+
kind: str(row, "kind"),
|
|
1804
|
+
role: str(row, "role"),
|
|
1805
|
+
sourceDir: str(row, "source_dir"),
|
|
1806
|
+
profileHash: str(row, "profile_hash"),
|
|
1807
|
+
warbleIdentity: str(row, "warble_identity"),
|
|
1808
|
+
admissionStatus: str(row, "admission_status"),
|
|
1809
|
+
admissionReason: strOrNull(row, "admission_reason"),
|
|
1810
|
+
entryKind: strOrNull(row, "entry_kind"),
|
|
1811
|
+
entryVerb: strOrNull(row, "entry_verb"),
|
|
1812
|
+
irVersion: strOrNull(row, "ir_version"),
|
|
1813
|
+
componentsJson: str(row, "components_json"),
|
|
1814
|
+
rulesVersion: row["rules_version"] === null || row["rules_version"] === undefined ? 0 : num(row, "rules_version"),
|
|
1815
|
+
createdAt: str(row, "created_at"),
|
|
1816
|
+
updatedAt: str(row, "updated_at"),
|
|
1817
|
+
admittedAt: strOrNull(row, "admitted_at"),
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1742
1820
|
function rowToNativeSetupRecovery(row) {
|
|
1743
1821
|
return {
|
|
1744
1822
|
sessionId: str(row, "session_id"),
|
|
@@ -1761,6 +1839,7 @@ function rowToNativeStructuredAnswer(row) {
|
|
|
1761
1839
|
envelopeJson: str(row, "envelope_json"),
|
|
1762
1840
|
digest: str(row, "digest"),
|
|
1763
1841
|
createdAt: str(row, "created_at"),
|
|
1842
|
+
grounded: intToBool(row["grounded"]),
|
|
1764
1843
|
};
|
|
1765
1844
|
}
|
|
1766
1845
|
function rowToEvent(row) {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { AdapterEnvStatus } from "./wire-types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The one credential env var for `"gateway"` mode, whatever pi-ai provider it
|
|
4
|
+
* targets. The BFF reads it and passes it to the adapter per request; pi-ai's
|
|
5
|
+
* own provider env vars are never consulted.
|
|
6
|
+
*/
|
|
7
|
+
export declare const GATEWAY_API_KEY_ENV = "GENBI_GATEWAY_API_KEY";
|
|
2
8
|
/**
|
|
3
9
|
* Reports which api-key adapters have their required credential env var present on the BFF
|
|
4
10
|
* process — booleans only. The key values themselves (or any prefix/suffix/length derived
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one credential env var for `"gateway"` mode, whatever pi-ai provider it
|
|
3
|
+
* targets. The BFF reads it and passes it to the adapter per request; pi-ai's
|
|
4
|
+
* own provider env vars are never consulted.
|
|
5
|
+
*/
|
|
6
|
+
export const GATEWAY_API_KEY_ENV = "GENBI_GATEWAY_API_KEY";
|
|
1
7
|
/** True iff the named env var is set to a non-empty (non-whitespace-only) value. */
|
|
2
8
|
function isNonEmptyEnv(name) {
|
|
3
9
|
const value = process.env[name];
|
|
@@ -13,5 +19,6 @@ export function detectAdapterEnv() {
|
|
|
13
19
|
return {
|
|
14
20
|
anthropic: isNonEmptyEnv("ANTHROPIC_API_KEY"),
|
|
15
21
|
openaiCompatible: isNonEmptyEnv("OPENAI_API_KEY"),
|
|
22
|
+
gateway: isNonEmptyEnv(GATEWAY_API_KEY_ENV),
|
|
16
23
|
};
|
|
17
24
|
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* forwards whatever the runtime actually reports.
|
|
15
15
|
*/
|
|
16
16
|
import type { AgentEvent, RouteResult, StepTrace } from "../harness/index.js";
|
|
17
|
-
import type { AnswerEvent, ArtifactEvent, ArtifactKind, RefusalEvent, ToolStep } from "./wire-types.js";
|
|
17
|
+
import type { AnswerEvent, ArtifactEvent, ArtifactKind, RefusalEvent, RenderEnvelope, ToolStep } from "./wire-types.js";
|
|
18
18
|
/**
|
|
19
19
|
* Canned remediation text for a refusal. `RefusalAgentEvent`/`RefusalResult`
|
|
20
20
|
* carry no remediation hint from the agent runtime — the UI's `fix` field is
|
|
@@ -37,9 +37,11 @@ export declare function extractTrace(result: RouteResult): StepTrace | undefined
|
|
|
37
37
|
* on the floor and the raw text always went out as `form: "text"`,
|
|
38
38
|
* `verified: false`). `extractEnvelopeFromText` recovers it — including
|
|
39
39
|
* `answer_query`'s flat `{columns, rows}` tool-contract shape, normalized
|
|
40
|
-
* into a `table` block
|
|
41
|
-
*
|
|
42
|
-
*
|
|
40
|
+
* into a `table` block. The recovered envelope's `verified` is model-written,
|
|
41
|
+
* so {@link withHostVerification} decides it from host evidence instead (see
|
|
42
|
+
* there). Only when
|
|
43
|
+
* no envelope can be recovered at all does the raw text fall back to
|
|
44
|
+
* `form: "text"`.
|
|
43
45
|
*
|
|
44
46
|
* That `form: "text"` fallback is the one case where "verified: false" is
|
|
45
47
|
* ambiguous between two very different situations: a pure conversational
|
|
@@ -51,6 +53,22 @@ export declare function extractTrace(result: RouteResult): StepTrace | undefined
|
|
|
51
53
|
* introspection and artifact-saving tool calls don't count as a data claim.
|
|
52
54
|
*/
|
|
53
55
|
export declare function toAnswerOrRefusalEvent(id: string, result: RouteResult, worklog?: readonly ToolStep[]): AnswerEvent | RefusalEvent;
|
|
56
|
+
/**
|
|
57
|
+
* The one place a subscription answer's `verified` is decided; a `verified`
|
|
58
|
+
* field in model-written text is never kept.
|
|
59
|
+
*
|
|
60
|
+
* - `codex-local`: the model's `run_sql` executes through this turn's host
|
|
61
|
+
* query service, so the answer is grounded against that turn's observations
|
|
62
|
+
* ({@link groundCodexLocalEnvelope}): verified only when every query it
|
|
63
|
+
* cites is one the host ran this turn and that read a table, and then its
|
|
64
|
+
* tables and definitions are the observed ones. Otherwise it stays as
|
|
65
|
+
* written, badged "Unverified".
|
|
66
|
+
* - `agent-sdk`: the host runs none of its queries and holds no evidence, so
|
|
67
|
+
* it is always `false`, with the rest of the envelope kept as-is.
|
|
68
|
+
*
|
|
69
|
+
* Other backends pass through.
|
|
70
|
+
*/
|
|
71
|
+
export declare function withHostVerification(result: RouteResult, envelope: RenderEnvelope): RenderEnvelope;
|
|
54
72
|
/** Maps a `RunFinishEvent`'s companion `ArtifactAgentEvent` straight through — no BFF-side transformation needed. */
|
|
55
73
|
export declare function toArtifactEvent(id: string, name: string, artifactKind: ArtifactKind, location: string, artifactId: string): ArtifactEvent;
|
|
56
74
|
export declare const DECISION_ROUTE_ID = "decision-route";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BUILD_DASHBOARD_TOOL_NAME, extractEnvelopeFromText, WREN_QUERY_TOOL_NAME } from "../harness/index.js";
|
|
2
|
+
import { groundCodexLocalEnvelope } from "./codex-local-grounding.js";
|
|
2
3
|
/**
|
|
3
4
|
* Canned remediation text for a refusal. `RefusalAgentEvent`/`RefusalResult`
|
|
4
5
|
* carry no remediation hint from the agent runtime — the UI's `fix` field is
|
|
@@ -35,9 +36,11 @@ export function extractTrace(result) {
|
|
|
35
36
|
* on the floor and the raw text always went out as `form: "text"`,
|
|
36
37
|
* `verified: false`). `extractEnvelopeFromText` recovers it — including
|
|
37
38
|
* `answer_query`'s flat `{columns, rows}` tool-contract shape, normalized
|
|
38
|
-
* into a `table` block
|
|
39
|
-
*
|
|
40
|
-
*
|
|
39
|
+
* into a `table` block. The recovered envelope's `verified` is model-written,
|
|
40
|
+
* so {@link withHostVerification} decides it from host evidence instead (see
|
|
41
|
+
* there). Only when
|
|
42
|
+
* no envelope can be recovered at all does the raw text fall back to
|
|
43
|
+
* `form: "text"`.
|
|
41
44
|
*
|
|
42
45
|
* That `form: "text"` fallback is the one case where "verified: false" is
|
|
43
46
|
* ambiguous between two very different situations: a pure conversational
|
|
@@ -49,10 +52,15 @@ export function extractTrace(result) {
|
|
|
49
52
|
* introspection and artifact-saving tool calls don't count as a data claim.
|
|
50
53
|
*/
|
|
51
54
|
export function toAnswerOrRefusalEvent(id, result, worklog = []) {
|
|
55
|
+
if (result.backend === "codex-app-server") {
|
|
56
|
+
return { id, kind: "answer", answer: result.envelope
|
|
57
|
+
? { form: "rich", envelope: result.envelope }
|
|
58
|
+
: { form: "text", text: result.finalText, verified: false, dataAnswer: result.dataAttempted === true || attemptedDataAccess(worklog) } };
|
|
59
|
+
}
|
|
52
60
|
if (result.backend === "agent-sdk" || result.backend === "codex-local") {
|
|
53
61
|
const envelope = extractEnvelopeFromText(result.finalText);
|
|
54
62
|
if (envelope !== undefined) {
|
|
55
|
-
return { id, kind: "answer", answer: { form: "rich", envelope } };
|
|
63
|
+
return { id, kind: "answer", answer: { form: "rich", envelope: withHostVerification(result, envelope) } };
|
|
56
64
|
}
|
|
57
65
|
return {
|
|
58
66
|
id,
|
|
@@ -65,6 +73,26 @@ export function toAnswerOrRefusalEvent(id, result, worklog = []) {
|
|
|
65
73
|
}
|
|
66
74
|
return { id, kind: "refusal", reason: result.reason, fix: REFUSAL_FIX_HINT };
|
|
67
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* The one place a subscription answer's `verified` is decided; a `verified`
|
|
78
|
+
* field in model-written text is never kept.
|
|
79
|
+
*
|
|
80
|
+
* - `codex-local`: the model's `run_sql` executes through this turn's host
|
|
81
|
+
* query service, so the answer is grounded against that turn's observations
|
|
82
|
+
* ({@link groundCodexLocalEnvelope}): verified only when every query it
|
|
83
|
+
* cites is one the host ran this turn and that read a table, and then its
|
|
84
|
+
* tables and definitions are the observed ones. Otherwise it stays as
|
|
85
|
+
* written, badged "Unverified".
|
|
86
|
+
* - `agent-sdk`: the host runs none of its queries and holds no evidence, so
|
|
87
|
+
* it is always `false`, with the rest of the envelope kept as-is.
|
|
88
|
+
*
|
|
89
|
+
* Other backends pass through.
|
|
90
|
+
*/
|
|
91
|
+
export function withHostVerification(result, envelope) {
|
|
92
|
+
if (result.backend === "codex-local")
|
|
93
|
+
return groundCodexLocalEnvelope(envelope, result.finalText, result.hostObservations ?? []);
|
|
94
|
+
return result.backend === "agent-sdk" ? { ...envelope, verified: false } : envelope;
|
|
95
|
+
}
|
|
68
96
|
/** In-process's native in-process tool names that constitute an actual data claim — schema browsing / artifact saving don't count. */
|
|
69
97
|
const DATA_ACCESS_TOOL_NAMES = new Set([
|
|
70
98
|
WREN_QUERY_TOOL_NAME,
|
|
@@ -139,6 +167,10 @@ export function clarifyDecisionStep(detail) {
|
|
|
139
167
|
* -> `undefined`, so no gate entry is appended.
|
|
140
168
|
*/
|
|
141
169
|
export function gateDecisionStep(result) {
|
|
170
|
+
if (result.backend === "codex-app-server")
|
|
171
|
+
return result.envelope?.verified === true
|
|
172
|
+
? { id: DECISION_GATE_ID, kind: "decision", label: "Verify gate", state: "done", detail: "Query rows and SQL captured from successful host execution; interpretation is not verified." }
|
|
173
|
+
: undefined;
|
|
142
174
|
if (result.backend === "agent-sdk" || result.backend === "codex-local")
|
|
143
175
|
return undefined;
|
|
144
176
|
if (result.kind === "refusal") {
|
|
@@ -317,7 +349,7 @@ function truncate(text, max) {
|
|
|
317
349
|
}
|
|
318
350
|
/** Short text used for `turns.answer_summary` — feeds `server/compose.ts`'s D3 context composition. Never the full envelope. */
|
|
319
351
|
export function summarizeResult(result) {
|
|
320
|
-
if (result.backend === "agent-sdk" || result.backend === "codex-local") {
|
|
352
|
+
if (result.backend === "agent-sdk" || (result.backend === "codex-local" || result.backend === "codex-app-server")) {
|
|
321
353
|
return truncate(result.finalText, SUMMARY_MAX_LENGTH);
|
|
322
354
|
}
|
|
323
355
|
if (result.kind === "answer") {
|
|
@@ -14,6 +14,6 @@ export declare function collectBundleTierNames(bundle: Bundle): string[];
|
|
|
14
14
|
* Keep the compiled bundle's declared profile tied to the BFF-selected
|
|
15
15
|
* purpose before exposing it through the read-only Harness DTO.
|
|
16
16
|
*/
|
|
17
|
-
export declare function assertHarnessBundlePurpose(bundle: Bundle, purpose: NativePurpose): void;
|
|
18
|
-
export declare function buildHarnessDto(bundle: Bundle, store: Store, baseRouteOptions: BaseRouteOptions, purpose?: NativePurpose, nativeReadiness?: NativeSessionReadiness, setupReadiness?: HarnessSetupReadiness): HarnessDto;
|
|
17
|
+
export declare function assertHarnessBundlePurpose(bundle: Bundle, purpose: NativePurpose, selectedProfile?: string): void;
|
|
18
|
+
export declare function buildHarnessDto(bundle: Bundle, store: Store, baseRouteOptions: BaseRouteOptions, purpose?: NativePurpose, nativeReadiness?: NativeSessionReadiness, setupReadiness?: HarnessSetupReadiness, selectedProfile?: string): HarnessDto;
|
|
19
19
|
export {};
|
|
@@ -33,7 +33,7 @@ import { createHash } from "node:crypto";
|
|
|
33
33
|
import path from "node:path";
|
|
34
34
|
import { describeConnection, resolveConnectionSource } from "./conn-config.js";
|
|
35
35
|
import { bundleFormatVersion, deriveAdapterSpec, findLockedGatedCheck } from "../harness/index.js";
|
|
36
|
-
import { assertHarnessPurposeProfile, NATIVE_DISPATCH_REGISTRY } from "./native-dispatch-registry.js";
|
|
36
|
+
import { assertHarnessPurposeProfile, HARNESS_PROFILE_IDENTITY_ERROR, NATIVE_DISPATCH_REGISTRY } from "./native-dispatch-registry.js";
|
|
37
37
|
import { collectBundleTierNames as collectCompiledBundleTierNames, effectiveTierModel } from "./runtime-binding.js";
|
|
38
38
|
/** snake_case/kebab-case id -> Title Case display name, e.g. "answer_query" -> "Answer Query". */
|
|
39
39
|
function humanize(id) {
|
|
@@ -146,8 +146,10 @@ function runtimeBackendAndLabel(authChoice) {
|
|
|
146
146
|
return { backend: "api-key", label: `API key (${authChoice.adapter})` };
|
|
147
147
|
case "local":
|
|
148
148
|
return { backend: "local", label: authChoice.endpoint ? `Local (${authChoice.endpoint})` : "Local" };
|
|
149
|
-
case "gateway":
|
|
150
|
-
|
|
149
|
+
case "gateway": {
|
|
150
|
+
const provider = authChoice.config?.["provider"];
|
|
151
|
+
return { backend: "gateway", label: typeof provider === "string" && provider.trim() ? `Gateway (pi-ai: ${provider})` : "Gateway" };
|
|
152
|
+
}
|
|
151
153
|
}
|
|
152
154
|
}
|
|
153
155
|
/**
|
|
@@ -354,7 +356,14 @@ export function collectBundleTierNames(bundle) {
|
|
|
354
356
|
* Keep the compiled bundle's declared profile tied to the BFF-selected
|
|
355
357
|
* purpose before exposing it through the read-only Harness DTO.
|
|
356
358
|
*/
|
|
357
|
-
export function assertHarnessBundlePurpose(bundle, purpose) {
|
|
359
|
+
export function assertHarnessBundlePurpose(bundle, purpose, selectedProfile) {
|
|
360
|
+
// A selected conversation profile is described by its registry id; the compiled bundle must
|
|
361
|
+
// carry exactly that id. Without a selection the purpose's fixed profile applies as before.
|
|
362
|
+
if (selectedProfile !== undefined && purpose === "analysis") {
|
|
363
|
+
if (bundle.profile !== selectedProfile)
|
|
364
|
+
throw new Error(HARNESS_PROFILE_IDENTITY_ERROR);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
358
367
|
assertHarnessPurposeProfile(purpose, bundle.profile);
|
|
359
368
|
}
|
|
360
369
|
function buildRuntime(bundle, baseRouteOptions, resolver) {
|
|
@@ -397,8 +406,25 @@ function setupRunnerTarget(authChoice) {
|
|
|
397
406
|
case "in-process": return { target: "in-process:setup", targetLabel: "In-process Setup runner" };
|
|
398
407
|
}
|
|
399
408
|
}
|
|
400
|
-
function buildHarnessPurpose(purpose, baseRouteOptions, nativeReadiness, setupReadiness) {
|
|
409
|
+
function buildHarnessPurpose(purpose, baseRouteOptions, nativeReadiness, setupReadiness, selectedProfile) {
|
|
401
410
|
const definition = NATIVE_DISPATCH_REGISTRY[purpose];
|
|
411
|
+
// A selected conversation profile is reported with its own id and per-profile readiness.
|
|
412
|
+
if (selectedProfile !== undefined && purpose === "analysis" && selectedProfile !== definition.profile) {
|
|
413
|
+
// Only the per-profile entry speaks for a selected profile. Falling back to the purpose-level
|
|
414
|
+
// row would report the default's availability under another profile's name and could hide a
|
|
415
|
+
// vendor refusal that applies to that profile alone.
|
|
416
|
+
const readiness = nativeReadiness?.profiles?.[selectedProfile];
|
|
417
|
+
const available = readiness?.available ?? false;
|
|
418
|
+
return {
|
|
419
|
+
purpose,
|
|
420
|
+
profile: selectedProfile,
|
|
421
|
+
scopeKind: definition.scopeKind,
|
|
422
|
+
executionKind: "native_session",
|
|
423
|
+
...(readiness?.target ? { target: readiness.target, targetLabel: readiness.targetLabel } : {}),
|
|
424
|
+
available,
|
|
425
|
+
...(!available ? { reason: readiness?.reason ?? NATIVE_PURPOSE_UNAVAILABLE_REASON } : {}),
|
|
426
|
+
};
|
|
427
|
+
}
|
|
402
428
|
if (purpose === "setup") {
|
|
403
429
|
const available = setupReadiness?.available ?? false;
|
|
404
430
|
return {
|
|
@@ -419,9 +445,9 @@ function buildHarnessPurpose(purpose, baseRouteOptions, nativeReadiness, setupRe
|
|
|
419
445
|
...(!available ? { reason: readiness?.reason ?? NATIVE_PURPOSE_UNAVAILABLE_REASON } : {}),
|
|
420
446
|
};
|
|
421
447
|
}
|
|
422
|
-
export function buildHarnessDto(bundle, store, baseRouteOptions, purpose = "analysis", nativeReadiness, setupReadiness) {
|
|
448
|
+
export function buildHarnessDto(bundle, store, baseRouteOptions, purpose = "analysis", nativeReadiness, setupReadiness, selectedProfile) {
|
|
423
449
|
const resolver = buildRealTierResolver(baseRouteOptions, store);
|
|
424
|
-
const purposeInfo = buildHarnessPurpose(purpose, baseRouteOptions, nativeReadiness, setupReadiness);
|
|
450
|
+
const purposeInfo = buildHarnessPurpose(purpose, baseRouteOptions, nativeReadiness, setupReadiness, selectedProfile);
|
|
425
451
|
return {
|
|
426
452
|
purpose: purposeInfo,
|
|
427
453
|
profile: buildProfile(bundle, baseRouteOptions, store, purpose),
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { EnrichmentBinding } from "./enrichment.js";
|
|
2
2
|
export { InteractiveLaunchError, legacyInteractiveWorkspace } from "./native-session-workspace.js";
|
|
3
3
|
import type { NativeSessionStateBase } from "./native-session-workspace.js";
|
|
4
|
+
import type { NativeChildEnvironment, NativeExecutableIdentity } from "./native-runtime-spec.js";
|
|
4
5
|
export declare const INTERACTIVE_TARGETS: readonly ["claude-code:interactive", "codex:interactive"];
|
|
5
6
|
export type InteractiveTarget = (typeof INTERACTIVE_TARGETS)[number];
|
|
6
7
|
export interface InteractiveLaunchSpec {
|
|
7
|
-
readonly version: "1" | "2" | "3" | "4";
|
|
8
|
+
readonly version: "1" | "2" | "3" | "4" | "5";
|
|
8
9
|
readonly target: InteractiveTarget;
|
|
9
10
|
readonly executable: "claude" | "codex";
|
|
10
11
|
readonly argv: readonly string[];
|
|
@@ -35,7 +36,7 @@ export interface PtyFactory {
|
|
|
35
36
|
cwd: string;
|
|
36
37
|
cols: number;
|
|
37
38
|
rows: number;
|
|
38
|
-
env?:
|
|
39
|
+
env?: NativeChildEnvironment;
|
|
39
40
|
}): PtyProcess;
|
|
40
41
|
}
|
|
41
42
|
/**
|
|
@@ -48,12 +49,12 @@ export interface PtyFactory {
|
|
|
48
49
|
* advertised xterm ANSI palette silently optional and allow a caller to
|
|
49
50
|
* weaken the native-terminal contract.
|
|
50
51
|
*/
|
|
51
|
-
export declare function nativeTerminalEnvironment(base?: NodeJS.ProcessEnv):
|
|
52
|
+
export declare function nativeTerminalEnvironment(base?: NodeJS.ProcessEnv | NativeChildEnvironment): NativeChildEnvironment;
|
|
52
53
|
/**
|
|
53
54
|
* Mirrors the PATH lookup a shell-free PTY launch relies on. A shell alias or
|
|
54
55
|
* function is deliberately not accepted: node-pty cannot execute either.
|
|
55
56
|
*/
|
|
56
|
-
export declare function interactiveExecutableAvailable(executable: "claude" | "codex", pathValue?: string
|
|
57
|
+
export declare function interactiveExecutableAvailable(executable: "claude" | "codex", pathValue?: string): boolean;
|
|
57
58
|
export interface InteractiveTerminalSession {
|
|
58
59
|
readonly id: string;
|
|
59
60
|
readonly capability: string;
|
|
@@ -115,10 +116,11 @@ export declare function launchFallbackCommand(spec: Pick<InteractiveLaunchSpec,
|
|
|
115
116
|
/** Parse exactly the public v1 producer contract; unknown fields fail closed. */
|
|
116
117
|
export declare function readInteractiveLaunchSpec(boundProject: string, requestedTarget: InteractiveTarget, materializationRoot?: string, materializationState?: NativeSessionStateBase): InteractiveLaunchSpec;
|
|
117
118
|
export declare function dispatchInteractiveArtifacts(input: {
|
|
118
|
-
readonly
|
|
119
|
+
readonly producer: NativeExecutableIdentity;
|
|
119
120
|
readonly irPath: string;
|
|
120
121
|
readonly target: InteractiveTarget;
|
|
121
122
|
readonly cwd: string;
|
|
123
|
+
readonly env: NativeChildEnvironment;
|
|
122
124
|
readonly boundProject?: string;
|
|
123
125
|
readonly materializationState?: NativeSessionStateBase;
|
|
124
126
|
}): Promise<void>;
|
|
@@ -134,6 +136,7 @@ export interface InteractivePreparationInput {
|
|
|
134
136
|
export interface InteractivePreparationDeps {
|
|
135
137
|
readonly getCurrentBinding: () => EnrichmentBinding | undefined;
|
|
136
138
|
readonly executableAvailable?: (executable: "claude" | "codex") => boolean;
|
|
139
|
+
readonly resolveExecutable?: (executable: "claude" | "codex") => NativeExecutableIdentity | undefined;
|
|
137
140
|
}
|
|
138
141
|
/**
|
|
139
142
|
* Materializes and validates the clipboard/launch contract without loading a
|
|
@@ -158,6 +161,7 @@ export declare function getInteractiveTerminalReadiness(input: {
|
|
|
158
161
|
readonly getCurrentBinding: () => EnrichmentBinding | undefined;
|
|
159
162
|
readonly terminalHostAvailable: () => Promise<boolean>;
|
|
160
163
|
readonly executableAvailable?: (executable: "claude" | "codex") => boolean;
|
|
164
|
+
readonly resolveExecutable?: (executable: "claude" | "codex") => NativeExecutableIdentity | undefined;
|
|
161
165
|
/** BFF-owned base; validation is non-mutating and never reads the project root. */
|
|
162
166
|
readonly materializationState?: NativeSessionStateBase;
|
|
163
167
|
}): Promise<Record<InteractiveTarget, InteractiveTargetReadiness>>;
|
|
@@ -168,7 +172,7 @@ export declare class InteractiveTerminalManager {
|
|
|
168
172
|
start(spec: InteractiveLaunchSpec, identity?: {
|
|
169
173
|
readonly id: string;
|
|
170
174
|
readonly capability: string;
|
|
171
|
-
}, env?: NodeJS.ProcessEnv, leasePolicy?: InteractiveTerminalLeasePolicy): InteractiveTerminalSession;
|
|
175
|
+
}, env?: NodeJS.ProcessEnv | NativeChildEnvironment, leasePolicy?: InteractiveTerminalLeasePolicy): InteractiveTerminalSession;
|
|
172
176
|
get(id: string): InteractiveTerminalSession | undefined;
|
|
173
177
|
/** Runtime changes revoke every non-durable compatibility terminal immediately. */
|
|
174
178
|
closeAll(): void;
|
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
import { randomUUID } from "node:crypto";
|
|
8
8
|
import { existsSync, realpathSync, readFileSync, statSync } from "node:fs";
|
|
9
9
|
import path from "node:path";
|
|
10
|
+
import os from "node:os";
|
|
10
11
|
import { spawn as spawnProcess } from "node:child_process";
|
|
11
12
|
export { InteractiveLaunchError, legacyInteractiveWorkspace } from "./native-session-workspace.js";
|
|
12
13
|
import { InteractiveLaunchError, nativeSessionStateBaseAvailable, validateLegacyInteractiveWorkspace } from "./native-session-workspace.js";
|
|
14
|
+
import { assertNativeExecutableIdentity, buildNativeChildEnvironment, nativeProcessEnvironment } from "./native-runtime-spec.js";
|
|
13
15
|
export const INTERACTIVE_TARGETS = ["claude-code:interactive", "codex:interactive"];
|
|
14
16
|
const EXECUTABLE = {
|
|
15
17
|
"claude-code:interactive": "claude",
|
|
@@ -25,15 +27,33 @@ const EXECUTABLE = {
|
|
|
25
27
|
* advertised xterm ANSI palette silently optional and allow a caller to
|
|
26
28
|
* weaken the native-terminal contract.
|
|
27
29
|
*/
|
|
28
|
-
export function nativeTerminalEnvironment(base
|
|
29
|
-
const
|
|
30
|
-
|
|
30
|
+
export function nativeTerminalEnvironment(base) {
|
|
31
|
+
const environment = base ?? buildNativeChildEnvironment({
|
|
32
|
+
toolDirectories: [path.dirname(process.execPath)],
|
|
33
|
+
home: os.homedir(),
|
|
34
|
+
});
|
|
35
|
+
const pathValue = environment.PATH ?? path.dirname(process.execPath);
|
|
36
|
+
const home = environment.HOME ?? os.homedir();
|
|
37
|
+
return Object.freeze({
|
|
38
|
+
PATH: pathValue,
|
|
39
|
+
HOME: home,
|
|
40
|
+
...(process.platform === "darwin" ? { USER: os.userInfo().username, LOGNAME: os.userInfo().username } : {}),
|
|
41
|
+
TERM: "xterm-256color",
|
|
42
|
+
COLORTERM: "truecolor",
|
|
43
|
+
...(environment.WREN_PROJECT_HOME ? { WREN_PROJECT_HOME: environment.WREN_PROJECT_HOME } : {}),
|
|
44
|
+
...(environment.WREN_HOME ? { WREN_HOME: environment.WREN_HOME } : {}),
|
|
45
|
+
...(environment.CODEX_HOME ? { CODEX_HOME: environment.CODEX_HOME } : {}),
|
|
46
|
+
...(environment.WARBLE_MCP_CONNECTION_CREDENTIAL ? { WARBLE_MCP_CONNECTION_CREDENTIAL: environment.WARBLE_MCP_CONNECTION_CREDENTIAL } : {}),
|
|
47
|
+
...(environment.WARBLE_SETUP_BOOTSTRAP_ROOT ? { WARBLE_SETUP_BOOTSTRAP_ROOT: environment.WARBLE_SETUP_BOOTSTRAP_ROOT } : {}),
|
|
48
|
+
...(environment.PYTHONNOUSERSITE ? { PYTHONNOUSERSITE: "1" } : {}),
|
|
49
|
+
...(environment.PYTHONUTF8 ? { PYTHONUTF8: "1" } : {}),
|
|
50
|
+
});
|
|
31
51
|
}
|
|
32
52
|
/**
|
|
33
53
|
* Mirrors the PATH lookup a shell-free PTY launch relies on. A shell alias or
|
|
34
54
|
* function is deliberately not accepted: node-pty cannot execute either.
|
|
35
55
|
*/
|
|
36
|
-
export function interactiveExecutableAvailable(executable, pathValue
|
|
56
|
+
export function interactiveExecutableAvailable(executable, pathValue) {
|
|
37
57
|
if (!pathValue)
|
|
38
58
|
return false;
|
|
39
59
|
return pathValue.split(path.delimiter).some((directory) => {
|
|
@@ -131,8 +151,9 @@ export async function dispatchInteractiveArtifacts(input) {
|
|
|
131
151
|
throw new InteractiveLaunchError("interactive enrichment materialization failed");
|
|
132
152
|
if (input.boundProject && input.materializationState)
|
|
133
153
|
validateLegacyInteractiveWorkspace(input.materializationState, input.boundProject, input.cwd);
|
|
154
|
+
assertNativeExecutableIdentity(input.producer);
|
|
134
155
|
await new Promise((resolve, reject) => {
|
|
135
|
-
const child = spawnProcess(input.
|
|
156
|
+
const child = spawnProcess(input.producer.executable, ["dispatch", input.irPath, "--target", input.target, "--out", input.cwd], { cwd: input.cwd, env: nativeProcessEnvironment(input.env), shell: false, stdio: "ignore" });
|
|
136
157
|
child.once("error", () => reject(new InteractiveLaunchError("interactive enrichment materialization failed")));
|
|
137
158
|
child.once("exit", (code) => code === 0 ? resolve() : reject(new InteractiveLaunchError("interactive enrichment materialization failed")));
|
|
138
159
|
});
|
|
@@ -144,15 +165,19 @@ export async function dispatchInteractiveArtifacts(input) {
|
|
|
144
165
|
*/
|
|
145
166
|
export async function prepareInteractiveHandoff(input, deps) {
|
|
146
167
|
const executableAvailable = deps.executableAvailable ?? interactiveExecutableAvailable;
|
|
147
|
-
|
|
168
|
+
const executable = deps.resolveExecutable?.(EXECUTABLE[input.target]);
|
|
169
|
+
if (!(executable ?? executableAvailable(EXECUTABLE[input.target]))) {
|
|
148
170
|
throw new InteractiveLaunchError(`the ${EXECUTABLE[input.target]} interactive CLI is not available on this machine`);
|
|
149
171
|
}
|
|
172
|
+
if (executable)
|
|
173
|
+
assertNativeExecutableIdentity(executable);
|
|
150
174
|
await input.materialize();
|
|
151
175
|
const current = deps.getCurrentBinding();
|
|
152
176
|
if (!current || current.path !== input.binding.path || current.identity !== input.binding.identity || current.generation !== input.binding.generation || current.revision !== input.binding.revision) {
|
|
153
177
|
throw new InteractiveLaunchError("interactive enrichment is stale because the bound project changed");
|
|
154
178
|
}
|
|
155
|
-
const
|
|
179
|
+
const parsed = readInteractiveLaunchSpec(input.binding.path, input.target, input.artifactRoot, input.materializationState);
|
|
180
|
+
const spec = executable ? { ...parsed, hostExecutable: executable.executable } : parsed;
|
|
156
181
|
return { spec, handoff: { target: spec.target, fallbackCommand: launchFallbackCommand(spec) } };
|
|
157
182
|
}
|
|
158
183
|
/**
|
|
@@ -175,7 +200,7 @@ export async function getInteractiveTerminalReadiness(input) {
|
|
|
175
200
|
const executableAvailable = input.executableAvailable ?? interactiveExecutableAvailable;
|
|
176
201
|
const result = {};
|
|
177
202
|
for (const target of INTERACTIVE_TARGETS) {
|
|
178
|
-
if (!executableAvailable(EXECUTABLE[target])) {
|
|
203
|
+
if (!(input.resolveExecutable?.(EXECUTABLE[target]) ?? executableAvailable(EXECUTABLE[target]))) {
|
|
179
204
|
const reason = `the ${EXECUTABLE[target]} interactive CLI is not available on this machine`;
|
|
180
205
|
result[target] = { copyAvailable: false, embeddedTerminalAvailable: false, copyReason: reason, embeddedTerminalReason: reason };
|
|
181
206
|
continue;
|