@wrenai/genbi 0.0.4 → 0.0.6
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 +65 -3
- 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 +43 -45
- 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/describe.js +45 -6
- package/dist-server/harness/route/dispatched.d.ts +20 -0
- package/dist-server/harness/route/dispatched.js +122 -24
- package/dist-server/harness/route/host-query.d.ts +93 -0
- package/dist-server/harness/route/host-query.js +257 -0
- 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 +8 -2
- package/dist-server/harness/route/types.d.ts +47 -1
- package/dist-server/harness/route/wren-host-proxy.d.ts +33 -0
- package/dist-server/harness/route/wren-host-proxy.js +228 -0
- package/dist-server/harness/session/run.js +30 -10
- package/dist-server/harness/setup/runner.d.ts +17 -3
- package/dist-server/harness/setup/runner.js +272 -89
- package/dist-server/harness/setup/yaml-scalar.d.ts +23 -0
- package/dist-server/harness/setup/yaml-scalar.js +62 -0
- package/dist-server/harness/tools/setup-native.d.ts +7 -0
- package/dist-server/harness/tools/setup-native.js +7 -4
- package/dist-server/server/adopt.js +1 -17
- package/dist-server/server/app.d.ts +1 -0
- package/dist-server/server/app.js +175 -17
- 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 +185 -25
- 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 +23 -4
- package/dist-server/server/fold.js +42 -5
- package/dist-server/server/harness.d.ts +2 -2
- package/dist-server/server/harness.js +33 -7
- package/dist-server/server/host-evidence-grounding.d.ts +21 -0
- package/dist-server/server/host-evidence-grounding.js +93 -0
- 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 +112 -7
- package/dist-server/server/native-artifacts.js +194 -14
- 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 +86 -26
- 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,23 @@ 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` and `agent-sdk`: the model's host query tool (codex:local's
|
|
61
|
+
* `run_sql`, the agent-sdk host MCP's `run_sql`) executes through this turn's
|
|
62
|
+
* host query service, so the answer is grounded against that turn's
|
|
63
|
+
* observations ({@link groundOnHostEvidence}): verified only when every
|
|
64
|
+
* query it cites is one the host ran this turn and that read a table, and
|
|
65
|
+
* then its tables and definitions are the observed ones. Otherwise it stays
|
|
66
|
+
* as written, badged "Unverified" — including any answer whose data came
|
|
67
|
+
* another way (agent-sdk's Bash `wren` stays available and yields no host
|
|
68
|
+
* evidence).
|
|
69
|
+
*
|
|
70
|
+
* Other backends pass through.
|
|
71
|
+
*/
|
|
72
|
+
export declare function withHostVerification(result: RouteResult, envelope: RenderEnvelope): RenderEnvelope;
|
|
54
73
|
/** Maps a `RunFinishEvent`'s companion `ArtifactAgentEvent` straight through — no BFF-side transformation needed. */
|
|
55
74
|
export declare function toArtifactEvent(id: string, name: string, artifactKind: ArtifactKind, location: string, artifactId: string): ArtifactEvent;
|
|
56
75
|
export declare const DECISION_ROUTE_ID = "decision-route";
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { BUILD_DASHBOARD_TOOL_NAME, extractEnvelopeFromText, WREN_QUERY_TOOL_NAME } from "../harness/index.js";
|
|
2
|
+
import { extractJsonCandidateFromText } from "../harness/render/envelope.js";
|
|
3
|
+
import { groundOnHostEvidence } from "./host-evidence-grounding.js";
|
|
2
4
|
/**
|
|
3
5
|
* Canned remediation text for a refusal. `RefusalAgentEvent`/`RefusalResult`
|
|
4
6
|
* carry no remediation hint from the agent runtime — the UI's `fix` field is
|
|
@@ -35,9 +37,11 @@ export function extractTrace(result) {
|
|
|
35
37
|
* on the floor and the raw text always went out as `form: "text"`,
|
|
36
38
|
* `verified: false`). `extractEnvelopeFromText` recovers it — including
|
|
37
39
|
* `answer_query`'s flat `{columns, rows}` tool-contract shape, normalized
|
|
38
|
-
* into a `table` block
|
|
39
|
-
*
|
|
40
|
-
*
|
|
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"`.
|
|
41
45
|
*
|
|
42
46
|
* That `form: "text"` fallback is the one case where "verified: false" is
|
|
43
47
|
* ambiguous between two very different situations: a pure conversational
|
|
@@ -49,10 +53,15 @@ export function extractTrace(result) {
|
|
|
49
53
|
* introspection and artifact-saving tool calls don't count as a data claim.
|
|
50
54
|
*/
|
|
51
55
|
export function toAnswerOrRefusalEvent(id, result, worklog = []) {
|
|
56
|
+
if (result.backend === "codex-app-server") {
|
|
57
|
+
return { id, kind: "answer", answer: result.envelope
|
|
58
|
+
? { form: "rich", envelope: result.envelope }
|
|
59
|
+
: { form: "text", text: result.finalText, verified: false, dataAnswer: result.dataAttempted === true || attemptedDataAccess(worklog) } };
|
|
60
|
+
}
|
|
52
61
|
if (result.backend === "agent-sdk" || result.backend === "codex-local") {
|
|
53
62
|
const envelope = extractEnvelopeFromText(result.finalText);
|
|
54
63
|
if (envelope !== undefined) {
|
|
55
|
-
return { id, kind: "answer", answer: { form: "rich", envelope } };
|
|
64
|
+
return { id, kind: "answer", answer: { form: "rich", envelope: withHostVerification(result, envelope) } };
|
|
56
65
|
}
|
|
57
66
|
return {
|
|
58
67
|
id,
|
|
@@ -65,11 +74,35 @@ export function toAnswerOrRefusalEvent(id, result, worklog = []) {
|
|
|
65
74
|
}
|
|
66
75
|
return { id, kind: "refusal", reason: result.reason, fix: REFUSAL_FIX_HINT };
|
|
67
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* The one place a subscription answer's `verified` is decided; a `verified`
|
|
79
|
+
* field in model-written text is never kept.
|
|
80
|
+
*
|
|
81
|
+
* - `codex-local` and `agent-sdk`: the model's host query tool (codex:local's
|
|
82
|
+
* `run_sql`, the agent-sdk host MCP's `run_sql`) executes through this turn's
|
|
83
|
+
* host query service, so the answer is grounded against that turn's
|
|
84
|
+
* observations ({@link groundOnHostEvidence}): verified only when every
|
|
85
|
+
* query it cites is one the host ran this turn and that read a table, and
|
|
86
|
+
* then its tables and definitions are the observed ones. Otherwise it stays
|
|
87
|
+
* as written, badged "Unverified" — including any answer whose data came
|
|
88
|
+
* another way (agent-sdk's Bash `wren` stays available and yields no host
|
|
89
|
+
* evidence).
|
|
90
|
+
*
|
|
91
|
+
* Other backends pass through.
|
|
92
|
+
*/
|
|
93
|
+
export function withHostVerification(result, envelope) {
|
|
94
|
+
if (result.backend === "codex-local" || result.backend === "agent-sdk") {
|
|
95
|
+
return groundOnHostEvidence(envelope, extractJsonCandidateFromText(result.finalText), result.hostObservations ?? []);
|
|
96
|
+
}
|
|
97
|
+
return envelope;
|
|
98
|
+
}
|
|
68
99
|
/** In-process's native in-process tool names that constitute an actual data claim — schema browsing / artifact saving don't count. */
|
|
69
100
|
const DATA_ACCESS_TOOL_NAMES = new Set([
|
|
70
101
|
WREN_QUERY_TOOL_NAME,
|
|
71
102
|
BUILD_DASHBOARD_TOOL_NAME,
|
|
72
103
|
"wren.run_sql",
|
|
104
|
+
// The agent-sdk Ask's host MCP query tool (see `harness/route/dispatched.ts`).
|
|
105
|
+
"mcp__wren_host__run_sql",
|
|
73
106
|
]);
|
|
74
107
|
/**
|
|
75
108
|
* Dispatched never produces those native tool names — the dispatched agent's `answer_query`/
|
|
@@ -139,6 +172,10 @@ export function clarifyDecisionStep(detail) {
|
|
|
139
172
|
* -> `undefined`, so no gate entry is appended.
|
|
140
173
|
*/
|
|
141
174
|
export function gateDecisionStep(result) {
|
|
175
|
+
if (result.backend === "codex-app-server")
|
|
176
|
+
return result.envelope?.verified === true
|
|
177
|
+
? { 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." }
|
|
178
|
+
: undefined;
|
|
142
179
|
if (result.backend === "agent-sdk" || result.backend === "codex-local")
|
|
143
180
|
return undefined;
|
|
144
181
|
if (result.kind === "refusal") {
|
|
@@ -317,7 +354,7 @@ function truncate(text, max) {
|
|
|
317
354
|
}
|
|
318
355
|
/** Short text used for `turns.answer_summary` — feeds `server/compose.ts`'s D3 context composition. Never the full envelope. */
|
|
319
356
|
export function summarizeResult(result) {
|
|
320
|
-
if (result.backend === "agent-sdk" || result.backend === "codex-local") {
|
|
357
|
+
if (result.backend === "agent-sdk" || (result.backend === "codex-local" || result.backend === "codex-app-server")) {
|
|
321
358
|
return truncate(result.finalText, SUMMARY_MAX_LENGTH);
|
|
322
359
|
}
|
|
323
360
|
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),
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { HostQueryObservation } from "../harness/route/host-query.js";
|
|
2
|
+
import type { RenderEnvelope } from "../harness/render/envelope.js";
|
|
3
|
+
/**
|
|
4
|
+
* Grounds an answer against the queries the host executed for its scope (one codex:local or
|
|
5
|
+
* agent-sdk Ask turn, or one native session), with the component path's predicate
|
|
6
|
+
* (`citedGroundedQuery`: a tool-proven definition that reads at least one table, cited by
|
|
7
|
+
* `query_id`, else by whitespace-insensitive SQL).
|
|
8
|
+
*
|
|
9
|
+
* Verified only when the answer cites at least one query and every citation grounds. The
|
|
10
|
+
* envelope is then rebuilt from the observations alone: one `table` and one `definition` block
|
|
11
|
+
* (carrying the observed `query_id`) per cited query, plus the model's `summary` text as written.
|
|
12
|
+
* A cited result the host saw truncated still grounds the rows it shows; the host then appends
|
|
13
|
+
* a note to `summary`, the only place the render contract carries such text. Anything else stays the model's answer with
|
|
14
|
+
* `verified: false`: no citation, a citation to a query this scope never ran (including one from
|
|
15
|
+
* another turn or session), a query that read no table, or chart / KPI blocks the host cannot rebuild.
|
|
16
|
+
*
|
|
17
|
+
* `raw` is the answer as written, before envelope normalization (see `citations`).
|
|
18
|
+
*/
|
|
19
|
+
export declare function groundOnHostEvidence(envelope: RenderEnvelope, raw: unknown, observations: readonly HostQueryObservation[]): RenderEnvelope;
|
|
20
|
+
/** Host-written, never model-written: a cited table that is only the first rows of its result. */
|
|
21
|
+
export declare function truncationNote(shown: number): string;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { citedGroundedQuery } from "../harness/components/normalize.js";
|
|
2
|
+
import { namedAnswerQueryId, namedAnswerSql, sqlKey } from "../harness/render/answering-query.js";
|
|
3
|
+
/** Blocks whose values the host cannot rebuild from one observed table; their presence leaves an answer unverified. */
|
|
4
|
+
const UNREBUILDABLE_DATA_BLOCKS = new Set(["chart", "kpi_card"]);
|
|
5
|
+
function record(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The queries an answer names. A `{blocks}` answer cites through its `definition` blocks; a flat
|
|
10
|
+
* or query-result answer through its own `definition` / top-level fields, read from `raw` (the
|
|
11
|
+
* answer as the model wrote it) because envelope normalization drops a cited `query_id`. A host
|
|
12
|
+
* id wins over SQL, like the component path.
|
|
13
|
+
*/
|
|
14
|
+
function citations(envelope, raw) {
|
|
15
|
+
if (!Array.isArray(record(raw)?.blocks)) {
|
|
16
|
+
const queryId = namedAnswerQueryId(raw);
|
|
17
|
+
if (queryId !== undefined)
|
|
18
|
+
return [{ queryId }];
|
|
19
|
+
const sql = namedAnswerSql(raw);
|
|
20
|
+
return sql !== undefined ? [{ sql }] : [];
|
|
21
|
+
}
|
|
22
|
+
const found = [];
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
for (const block of Array.isArray(envelope.blocks) ? envelope.blocks : []) {
|
|
25
|
+
const fields = record(block);
|
|
26
|
+
if (fields?.type !== "definition")
|
|
27
|
+
continue;
|
|
28
|
+
const queryId = typeof fields.query_id === "string" && fields.query_id.length > 0 ? fields.query_id : undefined;
|
|
29
|
+
const sql = typeof fields.sql === "string" && fields.sql.trim() ? fields.sql : undefined;
|
|
30
|
+
const citation = queryId !== undefined ? { queryId } : sql !== undefined ? { sql } : undefined;
|
|
31
|
+
if (!citation)
|
|
32
|
+
continue;
|
|
33
|
+
const key = queryId !== undefined ? `id:${queryId}` : `sql:${sqlKey(sql)}`;
|
|
34
|
+
if (seen.has(key))
|
|
35
|
+
continue;
|
|
36
|
+
seen.add(key);
|
|
37
|
+
found.push(citation);
|
|
38
|
+
}
|
|
39
|
+
return found;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Grounds an answer against the queries the host executed for its scope (one codex:local or
|
|
43
|
+
* agent-sdk Ask turn, or one native session), with the component path's predicate
|
|
44
|
+
* (`citedGroundedQuery`: a tool-proven definition that reads at least one table, cited by
|
|
45
|
+
* `query_id`, else by whitespace-insensitive SQL).
|
|
46
|
+
*
|
|
47
|
+
* Verified only when the answer cites at least one query and every citation grounds. The
|
|
48
|
+
* envelope is then rebuilt from the observations alone: one `table` and one `definition` block
|
|
49
|
+
* (carrying the observed `query_id`) per cited query, plus the model's `summary` text as written.
|
|
50
|
+
* A cited result the host saw truncated still grounds the rows it shows; the host then appends
|
|
51
|
+
* a note to `summary`, the only place the render contract carries such text. Anything else stays the model's answer with
|
|
52
|
+
* `verified: false`: no citation, a citation to a query this scope never ran (including one from
|
|
53
|
+
* another turn or session), a query that read no table, or chart / KPI blocks the host cannot rebuild.
|
|
54
|
+
*
|
|
55
|
+
* `raw` is the answer as written, before envelope normalization (see `citations`).
|
|
56
|
+
*/
|
|
57
|
+
export function groundOnHostEvidence(envelope, raw, observations) {
|
|
58
|
+
const unverified = { ...envelope, verified: false };
|
|
59
|
+
if (observations.length === 0)
|
|
60
|
+
return unverified;
|
|
61
|
+
const blocks = Array.isArray(envelope.blocks) ? envelope.blocks : [];
|
|
62
|
+
if (blocks.some((block) => UNREBUILDABLE_DATA_BLOCKS.has(String(record(block)?.type))))
|
|
63
|
+
return unverified;
|
|
64
|
+
const cited = citations(envelope, raw);
|
|
65
|
+
if (cited.length === 0)
|
|
66
|
+
return unverified;
|
|
67
|
+
const rebuilt = [];
|
|
68
|
+
const notes = [];
|
|
69
|
+
for (const citation of cited) {
|
|
70
|
+
const query = citedGroundedQuery(observations, citation);
|
|
71
|
+
if (!query)
|
|
72
|
+
return unverified;
|
|
73
|
+
const observation = observations.find((call) => call.output?.query_id === query.queryId);
|
|
74
|
+
if (observation?.output?.truncated === true)
|
|
75
|
+
notes.push(truncationNote(query.table.rows.length));
|
|
76
|
+
// The definition keeps the observed query's host id, so grounding the rebuilt envelope
|
|
77
|
+
// again (the artifact path does) resolves to the very same observation.
|
|
78
|
+
rebuilt.push({ type: "table", columns: query.table.columns, rows: query.table.rows }, { type: "definition", ...query.definition, ...(query.queryId !== undefined ? { query_id: query.queryId } : {}) });
|
|
79
|
+
}
|
|
80
|
+
const summary = withNotes(typeof envelope.summary === "string" ? envelope.summary : undefined, notes);
|
|
81
|
+
return { blocks: rebuilt, ...(summary !== undefined ? { summary } : {}), verified: true };
|
|
82
|
+
}
|
|
83
|
+
/** Host-written, never model-written: a cited table that is only the first rows of its result. */
|
|
84
|
+
export function truncationNote(shown) {
|
|
85
|
+
return `Host note: this query returned more than ${shown} rows; the table shows only the first ${shown}.`;
|
|
86
|
+
}
|
|
87
|
+
/** Appends each host note once, so grounding an already-grounded envelope again changes nothing. */
|
|
88
|
+
function withNotes(summary, notes) {
|
|
89
|
+
const missing = [...new Set(notes)].filter((note) => !summary?.includes(note));
|
|
90
|
+
if (missing.length === 0)
|
|
91
|
+
return summary;
|
|
92
|
+
return [summary, ...missing].filter((part) => part !== undefined && part.length > 0).join("\n\n");
|
|
93
|
+
}
|