@polderlabs/bizar-omp 0.8.0 → 0.9.0
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 +287 -305
- package/agents/bizar-architect.md +1 -1
- package/agents/bizar-implementer.md +12 -2
- package/agents/bizar-planner.md +1 -1
- package/agents/bizar-researcher.md +1 -1
- package/agents/bizar-reviewer.md +7 -1
- package/agents/bizar-security-reviewer.md +1 -1
- package/agents/bizar-verifier.md +4 -0
- package/dist/cli/agents-ink.d.ts +12 -0
- package/dist/cli/agents-ink.d.ts.map +1 -1
- package/dist/cli/agents-ink.js +22 -7
- package/dist/cli/agents-ink.js.map +1 -1
- package/dist/cli/agents.d.ts.map +1 -1
- package/dist/cli/agents.js +6 -2
- package/dist/cli/agents.js.map +1 -1
- package/dist/cli/doctor.d.ts +4 -4
- package/dist/cli/install.d.ts +1 -7
- package/dist/cli/install.d.ts.map +1 -1
- package/dist/cli/install.js +75 -81
- package/dist/cli/install.js.map +1 -1
- package/dist/cli/main.js +5 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/mobile-claim.d.ts.map +1 -1
- package/dist/cli/mobile-claim.js +26 -18
- package/dist/cli/mobile-claim.js.map +1 -1
- package/dist/cli/omb-daemon.d.ts.map +1 -1
- package/dist/cli/omb-daemon.js +43 -11
- package/dist/cli/omb-daemon.js.map +1 -1
- package/dist/cli/omb-mobile.d.ts.map +1 -1
- package/dist/cli/omb-mobile.js +9 -3
- package/dist/cli/omb-mobile.js.map +1 -1
- package/dist/cli/omb-runtime.d.ts.map +1 -1
- package/dist/cli/omb-runtime.js +2 -0
- package/dist/cli/omb-runtime.js.map +1 -1
- package/dist/cli/omb-sessions.js +18 -7
- package/dist/cli/omb-sessions.js.map +1 -1
- package/dist/cli/omb-update.d.ts +1 -1
- package/dist/cli/omb-update.d.ts.map +1 -1
- package/dist/cli/omb-update.js +7 -36
- package/dist/cli/omb-update.js.map +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +2 -1
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/update-ink.d.ts.map +1 -1
- package/dist/cli/update-ink.js +8 -23
- package/dist/cli/update-ink.js.map +1 -1
- package/dist/core/acceptance.d.ts +20 -1
- package/dist/core/acceptance.d.ts.map +1 -1
- package/dist/core/acceptance.js +50 -2
- package/dist/core/acceptance.js.map +1 -1
- package/dist/core/policies.d.ts +11 -0
- package/dist/core/policies.d.ts.map +1 -1
- package/dist/core/policies.js +41 -10
- package/dist/core/policies.js.map +1 -1
- package/dist/core/transitions.d.ts.map +1 -1
- package/dist/core/transitions.js +4 -3
- package/dist/core/transitions.js.map +1 -1
- package/dist/core/types.d.ts +64 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/extension.d.ts +4 -0
- package/dist/extension.d.ts.map +1 -1
- package/dist/extension.js +420 -78
- package/dist/extension.js.map +1 -1
- package/dist/omp/compatibility.d.ts +15 -5
- package/dist/omp/compatibility.d.ts.map +1 -1
- package/dist/omp/compatibility.js +12 -2
- package/dist/omp/compatibility.js.map +1 -1
- package/dist/omp/daemon-service.d.ts.map +1 -1
- package/dist/omp/daemon-service.js +151 -21
- package/dist/omp/daemon-service.js.map +1 -1
- package/dist/omp/daemon.d.ts +18 -4
- package/dist/omp/daemon.d.ts.map +1 -1
- package/dist/omp/daemon.js +209 -76
- package/dist/omp/daemon.js.map +1 -1
- package/dist/omp/dispatch-lifecycle.d.ts +61 -0
- package/dist/omp/dispatch-lifecycle.d.ts.map +1 -0
- package/dist/omp/dispatch-lifecycle.js +101 -0
- package/dist/omp/dispatch-lifecycle.js.map +1 -0
- package/dist/omp/model-role-assignment.d.ts +5 -0
- package/dist/omp/model-role-assignment.d.ts.map +1 -1
- package/dist/omp/model-role-assignment.js +5 -0
- package/dist/omp/model-role-assignment.js.map +1 -1
- package/dist/omp/model-roles.d.ts +8 -0
- package/dist/omp/model-roles.d.ts.map +1 -1
- package/dist/omp/model-roles.js +10 -0
- package/dist/omp/model-roles.js.map +1 -1
- package/dist/omp/omb-sessions.d.ts +8 -1
- package/dist/omp/omb-sessions.d.ts.map +1 -1
- package/dist/omp/omb-sessions.js +40 -23
- package/dist/omp/omb-sessions.js.map +1 -1
- package/dist/omp/omb-settings.d.ts +5 -1
- package/dist/omp/omb-settings.d.ts.map +1 -1
- package/dist/omp/omb-settings.js +75 -26
- package/dist/omp/omb-settings.js.map +1 -1
- package/dist/omp/omp-provider-config.d.ts +3 -0
- package/dist/omp/omp-provider-config.d.ts.map +1 -1
- package/dist/omp/omp-provider-config.js +80 -30
- package/dist/omp/omp-provider-config.js.map +1 -1
- package/dist/omp/rpc.d.ts +3 -0
- package/dist/omp/rpc.d.ts.map +1 -1
- package/dist/omp/session-ownership.d.ts +64 -0
- package/dist/omp/session-ownership.d.ts.map +1 -0
- package/dist/omp/session-ownership.js +269 -0
- package/dist/omp/session-ownership.js.map +1 -0
- package/dist/omp/task-model-selector.d.ts +58 -0
- package/dist/omp/task-model-selector.d.ts.map +1 -0
- package/dist/omp/task-model-selector.js +144 -0
- package/dist/omp/task-model-selector.js.map +1 -0
- package/dist/omp/tmux.d.ts.map +1 -1
- package/dist/omp/tmux.js +10 -5
- package/dist/omp/tmux.js.map +1 -1
- package/dist/omp/tray.d.ts +9 -1
- package/dist/omp/tray.d.ts.map +1 -1
- package/dist/omp/tray.js +210 -56
- package/dist/omp/tray.js.map +1 -1
- package/dist/scripts/emit-compatibility-receipt.js +13 -3
- package/dist/scripts/emit-compatibility-receipt.js.map +1 -1
- package/dist/services/benchmarks.d.ts +68 -0
- package/dist/services/benchmarks.d.ts.map +1 -0
- package/dist/services/benchmarks.js +105 -0
- package/dist/services/benchmarks.js.map +1 -0
- package/dist/services/budget.d.ts +40 -1
- package/dist/services/budget.d.ts.map +1 -1
- package/dist/services/budget.js +74 -3
- package/dist/services/budget.js.map +1 -1
- package/dist/services/change-impact.d.ts +33 -0
- package/dist/services/change-impact.d.ts.map +1 -0
- package/dist/services/change-impact.js +103 -0
- package/dist/services/change-impact.js.map +1 -0
- package/dist/services/efficiency.d.ts +101 -0
- package/dist/services/efficiency.d.ts.map +1 -0
- package/dist/services/efficiency.js +170 -0
- package/dist/services/efficiency.js.map +1 -0
- package/dist/services/hypothesis-ledger.d.ts +66 -0
- package/dist/services/hypothesis-ledger.d.ts.map +1 -0
- package/dist/services/hypothesis-ledger.js +92 -0
- package/dist/services/hypothesis-ledger.js.map +1 -0
- package/dist/services/lesson-extraction.d.ts +27 -0
- package/dist/services/lesson-extraction.d.ts.map +1 -0
- package/dist/services/lesson-extraction.js +49 -0
- package/dist/services/lesson-extraction.js.map +1 -0
- package/dist/services/parallel-work.d.ts +44 -0
- package/dist/services/parallel-work.d.ts.map +1 -0
- package/dist/services/parallel-work.js +68 -0
- package/dist/services/parallel-work.js.map +1 -0
- package/dist/services/pr-shepherd.d.ts +83 -0
- package/dist/services/pr-shepherd.d.ts.map +1 -0
- package/dist/services/pr-shepherd.js +129 -0
- package/dist/services/pr-shepherd.js.map +1 -0
- package/dist/services/recipes.d.ts +12 -2
- package/dist/services/recipes.d.ts.map +1 -1
- package/dist/services/recipes.js +100 -8
- package/dist/services/recipes.js.map +1 -1
- package/dist/services/run-contract.d.ts +18 -0
- package/dist/services/run-contract.d.ts.map +1 -0
- package/dist/services/run-contract.js +32 -0
- package/dist/services/run-contract.js.map +1 -0
- package/dist/services/snapshots.d.ts +78 -0
- package/dist/services/snapshots.d.ts.map +1 -0
- package/dist/services/snapshots.js +194 -0
- package/dist/services/snapshots.js.map +1 -0
- package/dist/services/specialist-contracts.d.ts +58 -0
- package/dist/services/specialist-contracts.d.ts.map +1 -0
- package/dist/services/specialist-contracts.js +109 -0
- package/dist/services/specialist-contracts.js.map +1 -0
- package/dist/services/stall-detector.d.ts +27 -0
- package/dist/services/stall-detector.d.ts.map +1 -0
- package/dist/services/stall-detector.js +53 -0
- package/dist/services/stall-detector.js.map +1 -0
- package/dist/storage/resources.d.ts +1 -0
- package/dist/storage/resources.d.ts.map +1 -1
- package/dist/storage/resources.js +13 -11
- package/dist/storage/resources.js.map +1 -1
- package/dist/tools/integrate.d.ts.map +1 -1
- package/dist/tools/integrate.js +8 -7
- package/dist/tools/integrate.js.map +1 -1
- package/dist/tools/verify.d.ts +5 -2
- package/dist/tools/verify.d.ts.map +1 -1
- package/dist/tools/verify.js +84 -14
- package/dist/tools/verify.js.map +1 -1
- package/dist/tools/workflow.d.ts +2 -2
- package/dist/tools/workflow.d.ts.map +1 -1
- package/dist/tools/workflow.js +7 -0
- package/dist/tools/workflow.js.map +1 -1
- package/docs/07-bizarharness-integration.md +12 -0
- package/docs/assets/bizar-omp-banner.svg +51 -18
- package/docs/compatibility/baseline.json +9 -8
- package/docs/compatibility/lifecycle-observation.md +15 -1
- package/docs/compatibility/supported-surfaces.json +11 -3
- package/docs/releases/0.8.1.md +53 -0
- package/docs/releases/0.9.0.md +35 -0
- package/docs/releases/support-matrix.md +1 -1
- package/docs/security/project-audit-2026-10-01.md +145 -0
- package/package.json +9 -5
- package/skills/bizar-omp/SKILL.md +54 -0
- package/skills/omp-native-development/SKILL.md +5 -3
- package/skills/omp-native-development/assets/tests/acceptance-matrix.json +417 -0
- package/skills/omp-native-development/references/accuracy-and-versioning.md +1 -1
- package/skills/omp-native-development/references/bizar-integration-contract.md +1 -1
- package/skills/omp-native-development/references/developer-handoff.md +3 -1
- package/skills/omp-native-development/references/execution-and-lifecycle.md +4 -0
- package/skills/omp-native-development/references/native-validation-matrix.md +25 -0
- package/skills/omp-native-development/references/sessions-sdk-rpc.md +17 -0
- package/skills/omp-native-development/references/settings-providers-security.md +11 -0
- package/skills/omp-native-development/references/source-manifest.json +20 -4
package/dist/extension.js
CHANGED
|
@@ -10,10 +10,21 @@ import { result } from "./tools/result.js";
|
|
|
10
10
|
import { runVerification } from "./tools/verify.js";
|
|
11
11
|
import { runWorkflow } from "./tools/workflow.js";
|
|
12
12
|
import { analyzeRecipe } from "./services/recipes.js";
|
|
13
|
-
import { freezeCheckSpec } from "./core/acceptance.js";
|
|
13
|
+
import { admittedRequirements, freezeCheckSpec, receiptIntegrityHash } from "./core/acceptance.js";
|
|
14
|
+
import { completableObjective } from "./services/run-contract.js";
|
|
14
15
|
import { freezeObjective } from "./core/transitions.js";
|
|
15
16
|
import { runCandidateCheck } from "./services/candidate-verify.js";
|
|
17
|
+
import { admitNativeTasks, settleNativeCall } from "./omp/dispatch-lifecycle.js";
|
|
18
|
+
import { readStructuredOutput } from "./services/specialist-contracts.js";
|
|
19
|
+
import { detectStall } from "./services/stall-detector.js";
|
|
20
|
+
import { lessonForNativeLearn, proposeLessons } from "./services/lesson-extraction.js";
|
|
21
|
+
import { analyzeChangeImpact } from "./services/change-impact.js";
|
|
22
|
+
import { createEfficiencyRecorder, recommendFromRoles, recommendFromTimeline, roleCostByDispatch } from "./services/efficiency.js";
|
|
23
|
+
import { captureSnapshot, listSnapshots, previewSnapshot, restoreSnapshot, sealSnapshot } from "./services/snapshots.js";
|
|
16
24
|
import { inspectEvidence } from "./services/inspector.js";
|
|
25
|
+
import { addHypothesis, attachEvidence, readyToImplement, recordRepair, requiresLedger, setHypothesisStatus } from "./services/hypothesis-ledger.js";
|
|
26
|
+
import { SCENARIOS, scoreAll, scoreScenario } from "./services/benchmarks.js";
|
|
27
|
+
import { auxiliaryWork, emptyShepherd, observeChecks, observeReviews, recordMerge, recordPush, recordRepair as recordShepherdRepair } from "./services/pr-shepherd.js";
|
|
17
28
|
import { projectWorkflow } from "./services/projection.js";
|
|
18
29
|
import { capabilityReport } from "./omp/capabilities.js";
|
|
19
30
|
import { repositoryIdentity, statePath, workingTreeDigest } from "./storage/repository-identity.js";
|
|
@@ -21,10 +32,12 @@ import { Journal } from "./storage/journal.js";
|
|
|
21
32
|
import { nativeSessionReference } from "./omp/context.js";
|
|
22
33
|
import { findOnPath } from "./omp/tmux.js";
|
|
23
34
|
import { acquireOmpSessionLease, clearOmbSessionReference, indexOmbSessionFile, isOmbSessionName, readOmbJournalNativeId, sameOmpSessionFile } from "./omp/omb-sessions.js";
|
|
35
|
+
import { normalizeRequestedModelSelectors } from "./omp/task-model-selector.js";
|
|
36
|
+
import { readJournalSessionId } from "./omp/session-ownership.js";
|
|
24
37
|
import { clearOmbHostGeneration, rebindOmbHostGeneration } from "./omp/tmux.js";
|
|
25
38
|
import { createSessionTitleController, resolveSessionTitleLabel } from "./omp/terminal-title.js";
|
|
26
39
|
import { bindSession } from "./omp/lifecycle.js";
|
|
27
|
-
import { BIZAR_MODEL_ROLE_AGENT_PROMPT, BIZAR_MODEL_ROLE_TOOL, BIZAR_MODEL_ROLES,
|
|
40
|
+
import { BIZAR_MODEL_ROLE_AGENT_PROMPT, BIZAR_MODEL_ROLE_TOOL, BIZAR_MODEL_ROLES, buildAgentEvaluationRequest, clipOperatorNotice, commitRolePlan, diffCatalog, formatCatalogDelta, formatRoleAssignmentPlan, planBizarModelRoles, planFromRecommendations, recordOf, reviewRoleRecommendations, roleAssignmentRoute, snapshotCatalog, isOpaqueOmniRouteCombo, omniRouteFailureGuidance, } from "./omp/model-role-assignment.js";
|
|
28
41
|
import { AGENT_ROLES } from "./omp/model-roles.js";
|
|
29
42
|
import { loadOmbSettings, recordCatalogObservation } from "./omp/omb-settings.js";
|
|
30
43
|
import { isRunPhaseActive } from "./core/types.js";
|
|
@@ -202,18 +215,6 @@ function applyBizarModelRoles(pi, assignments) {
|
|
|
202
215
|
settings.setModelRole(assignment.role, assignment.selector);
|
|
203
216
|
return "global";
|
|
204
217
|
}
|
|
205
|
-
async function setNativeSetting(pi, settings, key, value, cwd) {
|
|
206
|
-
if (settings?.set) {
|
|
207
|
-
settings.set(key, value);
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if (!pi.exec)
|
|
211
|
-
throw new ContractError(`OMP cannot update ${key}: its settings writer is unavailable`);
|
|
212
|
-
const command = findOnPath("omp", process.env) ?? "omp";
|
|
213
|
-
const result = await pi.exec(command, ["config", "set", key, JSON.stringify(value)], { cwd });
|
|
214
|
-
if (result.code !== 0)
|
|
215
|
-
throw new ContractError(`OMP could not update ${key} through its config command (exit ${result.code ?? "unknown"})`);
|
|
216
|
-
}
|
|
217
218
|
async function getNativeSetting(pi, settings, key, cwd) {
|
|
218
219
|
if (settings?.get)
|
|
219
220
|
return settings.get(key);
|
|
@@ -230,33 +231,6 @@ async function getNativeSetting(pi, settings, key, cwd) {
|
|
|
230
231
|
throw new ContractError(`OMP returned invalid JSON while reading ${key}`);
|
|
231
232
|
}
|
|
232
233
|
}
|
|
233
|
-
async function ensureBizarModelRoles(pi, cwd) {
|
|
234
|
-
const settings = pi.pi?.settings;
|
|
235
|
-
if (!settings && !pi.exec)
|
|
236
|
-
return;
|
|
237
|
-
const currentRoles = recordOf(await getNativeSetting(pi, settings, "modelRoles", cwd));
|
|
238
|
-
const missingRoles = Object.entries(BIZAR_MODEL_ROLES).filter(([role]) => typeof currentRoles[role] !== "string");
|
|
239
|
-
const projectStorage = await getNativeSetting(pi, settings, "modelRoleStorage", cwd) === "project";
|
|
240
|
-
const roleSetter = projectStorage ? settings?.setProjectModelRole : settings?.setModelRole;
|
|
241
|
-
if (roleSetter) {
|
|
242
|
-
for (const [role, selector] of missingRoles)
|
|
243
|
-
roleSetter.call(settings, role, selector);
|
|
244
|
-
}
|
|
245
|
-
const currentTags = recordOf(await getNativeSetting(pi, settings, "modelTags", cwd));
|
|
246
|
-
const nextTags = { ...currentTags };
|
|
247
|
-
for (const [role, tag] of Object.entries(BIZAR_MODEL_TAGS))
|
|
248
|
-
if (!Object.hasOwn(nextTags, role))
|
|
249
|
-
nextTags[role] = tag;
|
|
250
|
-
if (JSON.stringify(nextTags) !== JSON.stringify(currentTags))
|
|
251
|
-
await setNativeSetting(pi, settings, "modelTags", nextTags, cwd);
|
|
252
|
-
const currentCycle = await getNativeSetting(pi, settings, "cycleOrder", cwd);
|
|
253
|
-
const nextCycle = Array.isArray(currentCycle) ? currentCycle.filter((role) => typeof role === "string") : [];
|
|
254
|
-
for (const role of Object.keys(BIZAR_MODEL_ROLES))
|
|
255
|
-
if (!nextCycle.includes(role))
|
|
256
|
-
nextCycle.push(role);
|
|
257
|
-
if (JSON.stringify(nextCycle) !== JSON.stringify(currentCycle))
|
|
258
|
-
await setNativeSetting(pi, settings, "cycleOrder", nextCycle, cwd);
|
|
259
|
-
}
|
|
260
234
|
async function modelRoleHealth(pi, ctx, routeResponses) {
|
|
261
235
|
const settings = pi.pi?.settings;
|
|
262
236
|
if (!settings?.get && !pi.exec)
|
|
@@ -427,11 +401,30 @@ function applyOrchestratorPolicy(systemPrompt) {
|
|
|
427
401
|
}
|
|
428
402
|
return [MAIN_ORCHESTRATOR_POLICY];
|
|
429
403
|
}
|
|
404
|
+
/** Remove only Bizar's exact main-session block from a reused native prompt. */
|
|
405
|
+
function removeOrchestratorPolicy(systemPrompt) {
|
|
406
|
+
if (typeof systemPrompt === "string")
|
|
407
|
+
return systemPrompt.replace(MAIN_ORCHESTRATOR_POLICY, "");
|
|
408
|
+
if (Array.isArray(systemPrompt))
|
|
409
|
+
return systemPrompt.map(String).filter(prompt => prompt !== MAIN_ORCHESTRATOR_POLICY);
|
|
410
|
+
return [];
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* OMP's public extension context identifies both the session class and its
|
|
414
|
+
* host-owned session id. Do not substitute task depth, agent names, or process
|
|
415
|
+
* state: `/tan` children have depth zero and extension factories are rebound.
|
|
416
|
+
*/
|
|
417
|
+
function mainSessionId(ctx) {
|
|
418
|
+
if (ctx.agent?.kind !== "main")
|
|
419
|
+
return undefined;
|
|
420
|
+
const id = ctx.sessionManager?.getSessionId?.();
|
|
421
|
+
return typeof id === "string" && id.length > 0 ? id : undefined;
|
|
422
|
+
}
|
|
430
423
|
export default function bizarExtension(pi) {
|
|
431
424
|
pi.setLabel?.("Bizar Orchestrator");
|
|
432
425
|
let binding;
|
|
433
426
|
// Main orchestrator role is opt-in: task children must NEVER see it.
|
|
434
|
-
let
|
|
427
|
+
let orchestratorSessionId;
|
|
435
428
|
// The tab title is owned here because an extension override is rendered
|
|
436
429
|
// verbatim: OMP's own run-state spinner would never animate it. A Bizar host
|
|
437
430
|
// would otherwise show the bare binary name in every tab, with nothing to
|
|
@@ -487,7 +480,7 @@ export default function bizarExtension(pi) {
|
|
|
487
480
|
});
|
|
488
481
|
pi.on("session_start", async (_event, ctx) => {
|
|
489
482
|
binding = undefined;
|
|
490
|
-
|
|
483
|
+
orchestratorSessionId = undefined;
|
|
491
484
|
releaseNativeLease();
|
|
492
485
|
extensionUi = ctx.ui;
|
|
493
486
|
// The tab should name the conversation. OMP's own session name is the bare
|
|
@@ -497,6 +490,16 @@ export default function bizarExtension(pi) {
|
|
|
497
490
|
sessionTitle.setLabel(sessionTitleLabel(ctx));
|
|
498
491
|
const file = ctx.sessionManager?.getSessionFile?.();
|
|
499
492
|
if (file && existsSync(file)) {
|
|
493
|
+
// OMP has created this journal but has not written its session header
|
|
494
|
+
// yet. Ownership is keyed on that header, so there is no ownership
|
|
495
|
+
// domain to claim and no second writer to exclude; refusing here would
|
|
496
|
+
// shut down a healthy new session. It is registered instead, and OMP
|
|
497
|
+
// keeps writing until its header lands.
|
|
498
|
+
if (!readJournalSessionId(file)) {
|
|
499
|
+
currentNativeFile = file;
|
|
500
|
+
await registerNativeFile(ctx, file);
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
500
503
|
try {
|
|
501
504
|
nativeLease = acquireOmpSessionLease(file);
|
|
502
505
|
currentNativeFile = file;
|
|
@@ -507,12 +510,6 @@ export default function bizarExtension(pi) {
|
|
|
507
510
|
ctx.shutdown?.();
|
|
508
511
|
}
|
|
509
512
|
}
|
|
510
|
-
try {
|
|
511
|
-
await ensureBizarModelRoles(pi, ctx.cwd ?? process.cwd());
|
|
512
|
-
}
|
|
513
|
-
catch (error) {
|
|
514
|
-
ctx.ui?.notify(`Bizar could not apply its OMP settings; continuing without the failed setting update. ${error instanceof Error ? error.message : String(error)}`, "warning");
|
|
515
|
-
}
|
|
516
513
|
});
|
|
517
514
|
pi.on("session_before_switch", async (event, ctx) => {
|
|
518
515
|
binding = undefined;
|
|
@@ -540,6 +537,9 @@ export default function bizarExtension(pi) {
|
|
|
540
537
|
}
|
|
541
538
|
});
|
|
542
539
|
pi.on("session_switch", async (_event, ctx) => {
|
|
540
|
+
// OMP only emits this after a switch commits. Keep activation intact when a
|
|
541
|
+
// preflight is cancelled, but never carry it across a committed session.
|
|
542
|
+
orchestratorSessionId = undefined;
|
|
543
543
|
const file = ctx.sessionManager?.getSessionFile?.();
|
|
544
544
|
if (!file || !existsSync(file)) {
|
|
545
545
|
try {
|
|
@@ -626,17 +626,20 @@ export default function bizarExtension(pi) {
|
|
|
626
626
|
pi.on("session_shutdown", async () => {
|
|
627
627
|
releaseNativeLease();
|
|
628
628
|
binding = undefined;
|
|
629
|
-
|
|
629
|
+
orchestratorSessionId = undefined;
|
|
630
630
|
sessionTitle.dispose();
|
|
631
631
|
extensionUi = undefined;
|
|
632
632
|
});
|
|
633
633
|
pi.on("before_agent_start", (event, ctx) => {
|
|
634
634
|
extensionUi = ctx?.ui ?? extensionUi;
|
|
635
635
|
sessionTitle.setState("working");
|
|
636
|
+
const sessionId = mainSessionId(ctx ?? {});
|
|
637
|
+
const systemPrompt = paramsOf(event).systemPrompt;
|
|
638
|
+
const active = orchestratorSessionId !== undefined && orchestratorSessionId === sessionId;
|
|
636
639
|
return {
|
|
637
|
-
systemPrompt: appendUserIdentityInvariant(
|
|
638
|
-
? applyOrchestratorPolicy(appendBizarInvariant(
|
|
639
|
-
: appendBizarInvariant(
|
|
640
|
+
systemPrompt: appendUserIdentityInvariant(active
|
|
641
|
+
? applyOrchestratorPolicy(appendBizarInvariant(systemPrompt))
|
|
642
|
+
: appendBizarInvariant(removeOrchestratorPolicy(systemPrompt))),
|
|
640
643
|
};
|
|
641
644
|
});
|
|
642
645
|
pi.on("agent_end", (event, ctx) => {
|
|
@@ -658,6 +661,26 @@ export default function bizarExtension(pi) {
|
|
|
658
661
|
// previously carried its own underscored copy, so every valid call to a real
|
|
659
662
|
// packaged specialist was refused as a non-packaged agent.
|
|
660
663
|
const BIZAR_PACKAGED_AGENTS = Object.fromEntries(AGENT_ROLES.map(name => [name, true]));
|
|
664
|
+
/** Normalize one `task` item the way the host will read it, keeping every routing-relevant field. */
|
|
665
|
+
const requestedTask = (raw) => {
|
|
666
|
+
const entry = paramsOf(raw);
|
|
667
|
+
const requested = entry.model;
|
|
668
|
+
return {
|
|
669
|
+
name: typeof entry.name === "string" ? entry.name : undefined,
|
|
670
|
+
agent: typeof entry.agent === "string" ? entry.agent : "",
|
|
671
|
+
isolated: entry.isolated === true,
|
|
672
|
+
task: typeof entry.task === "string" ? entry.task : undefined,
|
|
673
|
+
solutionSpace: typeof entry.solutionSpace === "string" ? entry.solutionSpace : undefined,
|
|
674
|
+
// OMP 18.5 carries a per-item model selector that outranks the exact-agent
|
|
675
|
+
// override and the agent's own frontmatter. Dropping it would journal a
|
|
676
|
+
// Bizar-computed role while the host ran something else. The array is
|
|
677
|
+
// forwarded as sent rather than coerced, so a non-string entry is refused
|
|
678
|
+
// by the selector validator instead of being journaled as "[object Object]".
|
|
679
|
+
...(typeof requested === "string" ? { model: requested } : Array.isArray(requested) ? { model: requested } : {}),
|
|
680
|
+
...(typeof entry.effort === "string" ? { effort: entry.effort } : {}),
|
|
681
|
+
...(Array.isArray(entry.tools) ? { tools: entry.tools.map(toolName => String(toolName)) } : {}),
|
|
682
|
+
};
|
|
683
|
+
};
|
|
661
684
|
/**
|
|
662
685
|
* Every task in one call, in the shape the model sent it.
|
|
663
686
|
*
|
|
@@ -667,15 +690,7 @@ export default function bizarExtension(pi) {
|
|
|
667
690
|
* it, and treated a batch of individually-valid tasks as unadmitted because
|
|
668
691
|
* the top-level fields were absent.
|
|
669
692
|
*/
|
|
670
|
-
const requestedTasks = (input) =>
|
|
671
|
-
if (!Array.isArray(input.tasks)) {
|
|
672
|
-
return [{ agent: typeof input.agent === "string" ? input.agent : "", isolated: input.isolated === true }];
|
|
673
|
-
}
|
|
674
|
-
return input.tasks.map(entry => {
|
|
675
|
-
const task = paramsOf(entry);
|
|
676
|
-
return { agent: typeof task.agent === "string" ? task.agent : "", isolated: task.isolated === true };
|
|
677
|
-
});
|
|
678
|
-
};
|
|
693
|
+
const requestedTasks = (input) => Array.isArray(input.tasks) ? input.tasks.map(requestedTask) : [requestedTask(input)];
|
|
679
694
|
/**
|
|
680
695
|
* Name the run that closed the gate and how to reopen it.
|
|
681
696
|
*
|
|
@@ -698,13 +713,55 @@ export default function bizarExtension(pi) {
|
|
|
698
713
|
const run = active.journal.snapshot().run;
|
|
699
714
|
if (!isRunPhaseActive(run?.phase))
|
|
700
715
|
return;
|
|
716
|
+
// OMP rejects a batch-container `model` rather than dropping it. A managed
|
|
717
|
+
// run has to refuse it too, because the enclosing `catch` below treats an
|
|
718
|
+
// exception as "no opinion" and would otherwise let the call through with
|
|
719
|
+
// a selector Bizar never saw.
|
|
720
|
+
if (Array.isArray(paramsOf(params.input).tasks) && paramsOf(params.input).model !== undefined) {
|
|
721
|
+
return { block: true, reason: `A002: a batch \`task\` call cannot carry a top-level \`model\`; OMP rejects it instead of dropping it. Put \`model\` on each \`tasks[]\` item.${managedRunNotice(run)}` };
|
|
722
|
+
}
|
|
701
723
|
const tasks = requestedTasks(paramsOf(params.input));
|
|
702
724
|
for (const task of tasks) {
|
|
703
725
|
if (!task.isolated)
|
|
704
726
|
return { block: true, reason: `A002: managed workflow is active; native \`task\` calls must declare \`isolated: true\` on every task.${managedRunNotice(run)}` };
|
|
705
727
|
if (!BIZAR_PACKAGED_AGENTS[task.agent])
|
|
706
728
|
return { block: true, reason: `A002: managed workflow refused native \`task\` to non-packaged agent ${task.agent || "(unspecified)"}; only packaged Bizar specialists are admitted.${managedRunNotice(run)}` };
|
|
729
|
+
try {
|
|
730
|
+
normalizeRequestedModelSelectors(task.model);
|
|
731
|
+
}
|
|
732
|
+
catch (error) {
|
|
733
|
+
return { block: true, reason: `A002: ${error instanceof Error ? error.message : String(error)}.${managedRunNotice(run)}` };
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
// A002: the dispatch is journaled only once every task in the call is
|
|
737
|
+
// admitted, and before OMP runs any of them. A refused call leaves no
|
|
738
|
+
// dispatch behind, so nothing waits on work that will never happen.
|
|
739
|
+
try {
|
|
740
|
+
admitNativeTasks(active, run, tasks, String(params.toolCallId ?? ""));
|
|
707
741
|
}
|
|
742
|
+
catch (error) {
|
|
743
|
+
return { block: true, reason: `A002: ${error instanceof Error ? error.message : String(error)}.${managedRunNotice(run)}` };
|
|
744
|
+
}
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
catch {
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
// A002: settle on OMP's own tool result. The recorded outcome is the host's,
|
|
752
|
+
// never the model's, so a task that errored cannot settle as success.
|
|
753
|
+
pi.on("tool_result", (event, ctx) => {
|
|
754
|
+
try {
|
|
755
|
+
const params = paramsOf(event);
|
|
756
|
+
const toolCallId = String(params.toolCallId ?? "");
|
|
757
|
+
if (!toolCallId)
|
|
758
|
+
return;
|
|
759
|
+
const active = currentBinding(ctx);
|
|
760
|
+
const run = active.journal.snapshot().run;
|
|
761
|
+
if (!run || !isRunPhaseActive(run.phase))
|
|
762
|
+
return;
|
|
763
|
+
recorderFor(run.runId).callEnded(toolCallId, params.isError === true);
|
|
764
|
+
settleNativeCall(active, run, toolCallId, { isError: params.isError === true, ...readStructuredOutput(params.details), details: params.details });
|
|
708
765
|
}
|
|
709
766
|
catch {
|
|
710
767
|
return;
|
|
@@ -732,12 +789,81 @@ export default function bizarExtension(pi) {
|
|
|
732
789
|
return;
|
|
733
790
|
}
|
|
734
791
|
});
|
|
792
|
+
// A005: notice a repeating call before it burns more turns. This never blocks
|
|
793
|
+
// a call — a stalled agent is the one least able to recover from being
|
|
794
|
+
// refused — but the notice names the repetition and the way out.
|
|
795
|
+
const recentCalls = new Map();
|
|
796
|
+
const efficiency = new Map();
|
|
797
|
+
const recorderFor = (runId) => {
|
|
798
|
+
const existing = efficiency.get(runId);
|
|
799
|
+
if (existing)
|
|
800
|
+
return existing;
|
|
801
|
+
const recorder = createEfficiencyRecorder(runId);
|
|
802
|
+
efficiency.set(runId, recorder);
|
|
803
|
+
return recorder;
|
|
804
|
+
};
|
|
805
|
+
pi.on("tool_call", (event, ctx) => {
|
|
806
|
+
try {
|
|
807
|
+
const params = paramsOf(event);
|
|
808
|
+
const toolName = String(params.toolName ?? "");
|
|
809
|
+
if (!toolName)
|
|
810
|
+
return;
|
|
811
|
+
const active = currentBinding(ctx);
|
|
812
|
+
const run = active.journal.snapshot().run;
|
|
813
|
+
if (!isRunPhaseActive(run?.phase))
|
|
814
|
+
return;
|
|
815
|
+
// The fingerprint is a digest: the input may carry a credential, and the
|
|
816
|
+
// detector needs to compare calls, never to retain what they contained.
|
|
817
|
+
const fingerprint = createHash("sha256").update(JSON.stringify(params.input ?? null)).digest("hex").slice(0, 16);
|
|
818
|
+
// Only an admitted call is timed: a call the gates refused never ran, and
|
|
819
|
+
// charging the run for it would understate what it actually cost.
|
|
820
|
+
if (params.toolCallId)
|
|
821
|
+
recorderFor(run.runId).callStarted(String(params.toolCallId), toolName, fingerprint);
|
|
822
|
+
const history = [...(recentCalls.get(run.runId) ?? []), { toolName, fingerprint }].slice(-8);
|
|
823
|
+
recentCalls.set(run.runId, history);
|
|
824
|
+
const signal = detectStall(history);
|
|
825
|
+
if (signal)
|
|
826
|
+
ctx.ui?.notify(`${signal.message} ${signal.advice}`, "warning");
|
|
827
|
+
}
|
|
828
|
+
catch {
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
});
|
|
832
|
+
// A006: compaction is where Bizar's contract leaves the context window. It
|
|
833
|
+
// lives in the run journal, so it survives — but an agent working from a
|
|
834
|
+
// summary is working from something the gate never admitted. After a
|
|
835
|
+
// compaction that actually completed, say what the authoritative source is.
|
|
836
|
+
let compactionStarted;
|
|
837
|
+
pi.on("auto_compaction_start", (event) => {
|
|
838
|
+
const params = paramsOf(event);
|
|
839
|
+
compactionStarted = { reason: String(params.reason ?? "unknown"), action: String(params.action ?? "unknown") };
|
|
840
|
+
});
|
|
841
|
+
pi.on("auto_compaction_end", (event, ctx) => {
|
|
842
|
+
const params = paramsOf(event);
|
|
843
|
+
const started = compactionStarted;
|
|
844
|
+
compactionStarted = undefined;
|
|
845
|
+
if (params.aborted === true)
|
|
846
|
+
return;
|
|
847
|
+
const active = currentBinding(ctx);
|
|
848
|
+
const run = active.journal.snapshot().run;
|
|
849
|
+
if (!run || !isRunPhaseActive(run.phase))
|
|
850
|
+
return;
|
|
851
|
+
recorderFor(run.runId).compaction(started ? started.action : String(params.action ?? "auto"), false);
|
|
852
|
+
// Counted from the admitted objective rather than through the working-tree
|
|
853
|
+
// digest, so a session outside a repository still gets the pointer.
|
|
854
|
+
const admitted = run.objective.checkSpecs?.length ? run.objective.checkSpecs : run.objective.verificationPlan;
|
|
855
|
+
const criteria = (run.objective.criteria ?? []).filter(criterion => criterion.applicable).length;
|
|
856
|
+
ctx.ui?.notify(`Context compacted (${started ? `${started.reason}/${started.action}` : "auto"}). The admitted contract is in the run journal, not in this context: re-read it with bizar_inspect before continuing — ${admitted.length} admitted checks, ${criteria} criteria.`, "warning");
|
|
857
|
+
});
|
|
735
858
|
pi.registerTool({
|
|
736
859
|
name: "bizar_apply_main_orchestrator_role", label: "Bizar Apply Main Orchestrator Role",
|
|
737
860
|
description: "Activate the main orchestrator role in this session; required so that before_agent_start injects the orchestrator policy. Task children must never invoke this.",
|
|
738
861
|
parameters: pi.zod.object({}),
|
|
739
|
-
async execute(_id, _raw, _signal, _onUpdate,
|
|
740
|
-
|
|
862
|
+
async execute(_id, _raw, _signal, _onUpdate, ctx) {
|
|
863
|
+
const sessionId = mainSessionId(ctx);
|
|
864
|
+
if (!sessionId)
|
|
865
|
+
return failure(new ContractError("The main orchestrator role can only be activated from an identified OMP main session; subagents and hosts without a native session identity are refused."));
|
|
866
|
+
orchestratorSessionId = sessionId;
|
|
741
867
|
return result({ orchestratorActive: true, scope: "main-session-only", note: "Main orchestrator policy will be appended to subsequent agent system prompts in this session. Task children must not invoke this tool." });
|
|
742
868
|
},
|
|
743
869
|
});
|
|
@@ -778,15 +904,17 @@ export default function bizarExtension(pi) {
|
|
|
778
904
|
const receiptId = `receipt_${randomUUID()}`;
|
|
779
905
|
const issuedAt = new Date().toISOString();
|
|
780
906
|
const verdict = String(p.verdict);
|
|
781
|
-
const signedContent = JSON.stringify({ workItemId: p.workItemId, evidenceId, verdict, operatorId, issuedAt });
|
|
782
|
-
const signedContentHash = createHash("sha256").update(signedContent).digest("hex");
|
|
783
907
|
const receipt = {
|
|
784
908
|
id: receiptId, runId: state.run.runId, workItemId: String(p.workItemId),
|
|
785
909
|
evidenceId, verdict, rationale: String(p.rationale),
|
|
786
|
-
operatorId, signedContentHash, issuedAt,
|
|
910
|
+
operatorId, signedContentHash: "", issuedAt,
|
|
911
|
+
authority: "environment-configured-identity",
|
|
787
912
|
};
|
|
913
|
+
// Hash the assembled receipt so the digest covers every field the
|
|
914
|
+
// verdict asserts, and acceptance can re-derive it later.
|
|
915
|
+
receipt.signedContentHash = receiptIntegrityHash(receipt);
|
|
788
916
|
active.journal.append({ type: "operator.receipt.recorded", runId: state.run.runId, receipt }, `receipt:${receiptId}`, state.revision);
|
|
789
|
-
return result({ receiptId, verdict, operatorId, signedContentHash });
|
|
917
|
+
return result({ receiptId, verdict, operatorId, signedContentHash: receipt.signedContentHash, authority: receipt.authority });
|
|
790
918
|
}
|
|
791
919
|
catch (error) {
|
|
792
920
|
return failure(error);
|
|
@@ -796,7 +924,7 @@ export default function bizarExtension(pi) {
|
|
|
796
924
|
pi.registerTool({
|
|
797
925
|
name: "bizar_workflow", label: "Bizar Workflow", description: "Create, inspect, and advance a typed Bizar workflow; transitions are journaled and revision checked.",
|
|
798
926
|
parameters: pi.zod.object({
|
|
799
|
-
action: pi.zod.enum(["start", "status", "transition", "add-work", "accept-work", "complete", "cancel", "next", "amend-objective"]),
|
|
927
|
+
action: pi.zod.enum(["start", "status", "transition", "add-work", "accept-work", "complete", "cancel", "next", "next-batch", "amend-objective"]),
|
|
800
928
|
objective: pi.zod.union([pi.zod.string(), objectiveSchema(pi.zod)]).optional(),
|
|
801
929
|
to: pi.zod.string().optional(),
|
|
802
930
|
reason: pi.zod.string().optional(),
|
|
@@ -805,13 +933,14 @@ export default function bizarExtension(pi) {
|
|
|
805
933
|
evidenceIds: pi.zod.array(pi.zod.string()).optional(),
|
|
806
934
|
requiredChecks: pi.zod.array(pi.zod.string()).optional(),
|
|
807
935
|
by: pi.zod.enum(["criterion", "check-spec"]).optional(),
|
|
936
|
+
capacity: pi.zod.number().optional(),
|
|
808
937
|
}),
|
|
809
938
|
async execute(_id, raw, _signal, _onUpdate, ctx) {
|
|
810
939
|
try {
|
|
811
940
|
const active = currentBinding(ctx);
|
|
812
941
|
const p = paramsOf(raw);
|
|
813
942
|
const action = String(p.action);
|
|
814
|
-
const payload = action === "start" ? { ...p, objective: objectiveOf(p.objective) } : p;
|
|
943
|
+
const payload = action === "start" ? { ...p, objective: completableObjective(objectiveOf(p.objective), ctx.cwd ?? active.cwd) } : p;
|
|
815
944
|
return result(runWorkflow(active, action, payload));
|
|
816
945
|
}
|
|
817
946
|
catch (error) {
|
|
@@ -928,16 +1057,225 @@ export default function bizarExtension(pi) {
|
|
|
928
1057
|
}
|
|
929
1058
|
},
|
|
930
1059
|
});
|
|
1060
|
+
pi.registerTool({
|
|
1061
|
+
name: "bizar_propose_lesson", label: "Bizar Propose Lesson",
|
|
1062
|
+
description: "Derive post-run lessons from accepted work and the evidence that proved it. Hand each returned rule to OMP's native `learn` tool; OMP owns memory, Bizar owns the derivation.",
|
|
1063
|
+
parameters: pi.zod.object({}),
|
|
1064
|
+
async execute(_id, _raw, _signal, _onUpdate, ctx) {
|
|
1065
|
+
try {
|
|
1066
|
+
const active = currentBinding(ctx);
|
|
1067
|
+
const state = active.journal.snapshot();
|
|
1068
|
+
if (!state.run)
|
|
1069
|
+
throw new ContractError("No workflow run exists; start one first");
|
|
1070
|
+
const lessons = proposeLessons(state, state.run);
|
|
1071
|
+
return result({
|
|
1072
|
+
count: lessons.length,
|
|
1073
|
+
lessons: lessons.map(lessonForNativeLearn),
|
|
1074
|
+
source: lessons,
|
|
1075
|
+
note: lessons.length
|
|
1076
|
+
? "Pass each rule to OMP's `learn` tool; Bizar derives lessons from evidence and never writes memory itself."
|
|
1077
|
+
: "No accepted work carries passing evidence yet, so there is nothing to learn from without laundering an assertion.",
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
catch (error) {
|
|
1081
|
+
return failure(error);
|
|
1082
|
+
}
|
|
1083
|
+
},
|
|
1084
|
+
});
|
|
931
1085
|
pi.registerTool({
|
|
932
1086
|
name: "bizar_inspect", label: "Bizar Inspector", description: "Explain acceptance blockers and evidence lineage using the same decision function as completion.",
|
|
933
|
-
parameters: pi.zod.object({
|
|
1087
|
+
parameters: pi.zod.object({}),
|
|
934
1088
|
async execute(_id, raw, _signal, _onUpdate, ctx) {
|
|
935
1089
|
try {
|
|
936
1090
|
const active = currentBinding(ctx);
|
|
937
|
-
const
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1091
|
+
const state = active.journal.snapshot();
|
|
1092
|
+
if (!state.run)
|
|
1093
|
+
throw new ContractError("No workflow run exists; start one first");
|
|
1094
|
+
// The admitted contract, not the caller's list. A caller able to narrow
|
|
1095
|
+
// the requirement set could report a run as unblocked that completion
|
|
1096
|
+
// will refuse.
|
|
1097
|
+
const requirements = admittedRequirements(state.run, state.run.workItems, workingTreeDigest(active.cwd));
|
|
1098
|
+
return result({ inspection: inspectEvidence(state, requirements, active), projection: projectWorkflow(state, requirements) });
|
|
1099
|
+
}
|
|
1100
|
+
catch (error) {
|
|
1101
|
+
return failure(error);
|
|
1102
|
+
}
|
|
1103
|
+
},
|
|
1104
|
+
});
|
|
1105
|
+
pi.registerTool({
|
|
1106
|
+
name: "bizar_change_impact", label: "Bizar Change Impact",
|
|
1107
|
+
description: "Read the changed paths' blast radius from the tree: trust boundaries, entry points, workspace crossings and the method tier that follows.",
|
|
1108
|
+
parameters: pi.zod.object({}),
|
|
1109
|
+
async execute(_id, _raw, _signal, _onUpdate, ctx) {
|
|
1110
|
+
try {
|
|
1111
|
+
return result(analyzeChangeImpact(ctx.cwd ?? process.cwd()));
|
|
1112
|
+
}
|
|
1113
|
+
catch (error) {
|
|
1114
|
+
return failure(error);
|
|
1115
|
+
}
|
|
1116
|
+
},
|
|
1117
|
+
});
|
|
1118
|
+
pi.registerTool({
|
|
1119
|
+
name: "bizar_run_efficiency", label: "Bizar Run Efficiency",
|
|
1120
|
+
description: "Report what this managed run actually spent time on: call durations, failures, repeated inputs and compactions, with advisory recommendations and the signals the host cannot expose.",
|
|
1121
|
+
parameters: pi.zod.object({}),
|
|
1122
|
+
async execute(_id, _raw, _signal, _onUpdate, ctx) {
|
|
1123
|
+
try {
|
|
1124
|
+
const active = currentBinding(ctx);
|
|
1125
|
+
const state = active.journal.snapshot();
|
|
1126
|
+
if (!state.run)
|
|
1127
|
+
throw new ContractError("No workflow run exists; start one first");
|
|
1128
|
+
const timeline = recorderFor(state.run.runId).timeline();
|
|
1129
|
+
const costs = roleCostByDispatch(timeline, state.dispatches);
|
|
1130
|
+
return result({ ...timeline, recommendations: recommendFromTimeline(timeline), roleCosts: costs, roleRecommendations: recommendFromRoles(costs) });
|
|
1131
|
+
}
|
|
1132
|
+
catch (error) {
|
|
1133
|
+
return failure(error);
|
|
1134
|
+
}
|
|
1135
|
+
},
|
|
1136
|
+
});
|
|
1137
|
+
pi.registerTool({
|
|
1138
|
+
name: "bizar_edit_snapshot", label: "Bizar Edit Snapshot",
|
|
1139
|
+
description: "Capture a bounded pre-edit baseline for the paths a focused task will touch, seal it after the edit, and undo exactly those paths when the change turns out to be wrong.",
|
|
1140
|
+
parameters: pi.zod.object({
|
|
1141
|
+
action: pi.zod.enum(["capture", "seal", "preview", "restore", "list"]),
|
|
1142
|
+
id: pi.zod.string().optional(),
|
|
1143
|
+
paths: pi.zod.array(pi.zod.string()).optional(),
|
|
1144
|
+
}),
|
|
1145
|
+
async execute(_id, raw, _signal, _onUpdate, ctx) {
|
|
1146
|
+
try {
|
|
1147
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
1148
|
+
const params = paramsOf(raw);
|
|
1149
|
+
const action = String(params.action);
|
|
1150
|
+
if (action === "list")
|
|
1151
|
+
return result({ snapshots: listSnapshots(cwd) });
|
|
1152
|
+
if (action === "capture") {
|
|
1153
|
+
if (!Array.isArray(params.paths) || params.paths.length === 0)
|
|
1154
|
+
throw new ContractError("capture needs the paths the task will edit");
|
|
1155
|
+
return result({ snapshot: captureSnapshot(cwd, params.paths.map(String), { runId: currentBinding(ctx).journal.snapshot().run?.runId }) });
|
|
1156
|
+
}
|
|
1157
|
+
const snapshotId = String(params.id ?? "");
|
|
1158
|
+
if (!snapshotId)
|
|
1159
|
+
throw new ContractError(`The ${action} action needs a snapshot id`);
|
|
1160
|
+
if (action === "seal")
|
|
1161
|
+
return result({ snapshot: sealSnapshot(cwd, snapshotId) });
|
|
1162
|
+
if (action === "preview")
|
|
1163
|
+
return result(previewSnapshot(cwd, snapshotId));
|
|
1164
|
+
return result(restoreSnapshot(cwd, snapshotId));
|
|
1165
|
+
}
|
|
1166
|
+
catch (error) {
|
|
1167
|
+
return failure(error);
|
|
1168
|
+
}
|
|
1169
|
+
},
|
|
1170
|
+
});
|
|
1171
|
+
/** Run-scoped ledgers. Survives compaction and session switch; does not outlive the host process. */
|
|
1172
|
+
const hypothesisLedgers = new Map();
|
|
1173
|
+
/** Run-scoped shepherd state. Bizar holds policy here; the host owns the GitHub calls. */
|
|
1174
|
+
const shepherds = new Map();
|
|
1175
|
+
pi.registerTool({
|
|
1176
|
+
name: "bizar_hypothesis_ledger", label: "Bizar Hypothesis Ledger",
|
|
1177
|
+
description: "Track competing root-cause hypotheses and the repairs attempted against them, so an unknown-cause bug stops turning into patch churn.",
|
|
1178
|
+
parameters: pi.zod.object({
|
|
1179
|
+
action: pi.zod.enum(["required", "add", "attach", "support", "refute", "repair", "status"]),
|
|
1180
|
+
statement: pi.zod.string().optional(),
|
|
1181
|
+
hypothesisId: pi.zod.string().optional(),
|
|
1182
|
+
evidenceIds: pi.zod.array(pi.zod.string()).optional(),
|
|
1183
|
+
outcome: pi.zod.enum(["failed", "confirmed", "inconclusive"]).optional(),
|
|
1184
|
+
note: pi.zod.string().optional(),
|
|
1185
|
+
text: pi.zod.string().optional(),
|
|
1186
|
+
}),
|
|
1187
|
+
async execute(_id, raw, _signal, _onUpdate, ctx) {
|
|
1188
|
+
try {
|
|
1189
|
+
const params = paramsOf(raw);
|
|
1190
|
+
const action = String(params.action);
|
|
1191
|
+
if (action === "required")
|
|
1192
|
+
return result({ required: requiresLedger({ text: String(params.text ?? "") }) });
|
|
1193
|
+
const active = currentBinding(ctx);
|
|
1194
|
+
const state = active.journal.snapshot();
|
|
1195
|
+
if (!state.run)
|
|
1196
|
+
throw new ContractError("No workflow run exists; start one first");
|
|
1197
|
+
const runId = state.run.runId;
|
|
1198
|
+
const ledgers = hypothesisLedgers.get(runId) ?? { hypotheses: [], attempts: [] };
|
|
1199
|
+
const evidenceIds = Array.isArray(params.evidenceIds) ? params.evidenceIds.map(String) : [];
|
|
1200
|
+
const hypothesisId = String(params.hypothesisId ?? "");
|
|
1201
|
+
const save = (ledger, extra = {}) => { hypothesisLedgers.set(runId, ledger); return result({ ledger, readyToImplement: readyToImplement(ledger), ...extra }); };
|
|
1202
|
+
if (action === "status")
|
|
1203
|
+
return save(ledgers);
|
|
1204
|
+
if (action === "add")
|
|
1205
|
+
return save(addHypothesis(ledgers, String(params.statement ?? ""), evidenceIds));
|
|
1206
|
+
if (action === "attach")
|
|
1207
|
+
return save(attachEvidence(ledgers, hypothesisId, evidenceIds));
|
|
1208
|
+
if (action === "support" || action === "refute")
|
|
1209
|
+
return save(setHypothesisStatus(ledgers, hypothesisId, action === "support" ? "supported" : "refuted", String(params.note ?? "")));
|
|
1210
|
+
const outcome = recordRepair(ledgers, hypothesisId, params.outcome === "confirmed" || params.outcome === "inconclusive" ? params.outcome : "failed", String(params.note ?? ""));
|
|
1211
|
+
return save(outcome.ledger, { advice: outcome.advice });
|
|
1212
|
+
}
|
|
1213
|
+
catch (error) {
|
|
1214
|
+
return failure(error);
|
|
1215
|
+
}
|
|
1216
|
+
},
|
|
1217
|
+
});
|
|
1218
|
+
pi.registerTool({
|
|
1219
|
+
name: "bizar_benchmarks", label: "Bizar Behaviour Benchmarks",
|
|
1220
|
+
description: "Score this run's observed behaviour against the scenarios the operator actually pays for: avoidable spawns, repeated calls, retry loops and repository-wide gates.",
|
|
1221
|
+
parameters: pi.zod.object({ scenarioId: pi.zod.string().optional() }),
|
|
1222
|
+
async execute(_id, raw, _signal, _onUpdate, ctx) {
|
|
1223
|
+
try {
|
|
1224
|
+
const active = currentBinding(ctx);
|
|
1225
|
+
const state = active.journal.snapshot();
|
|
1226
|
+
if (!state.run)
|
|
1227
|
+
throw new ContractError("No workflow run exists; start one first");
|
|
1228
|
+
const timeline = recorderFor(state.run.runId).timeline();
|
|
1229
|
+
const observed = {
|
|
1230
|
+
toolCalls: timeline.entries.filter(entry => entry.kind === "tool" && entry.tool !== undefined).map(entry => ({ tool: entry.tool, fingerprint: entry.fingerprint ?? "", ...(entry.failed === undefined ? {} : { failed: entry.failed }) })),
|
|
1231
|
+
spawnedAgents: state.dispatches.length,
|
|
1232
|
+
validatedChecks: [...new Set(state.evidence.map(record => record.checkId))],
|
|
1233
|
+
totals: timeline.totals,
|
|
1234
|
+
};
|
|
1235
|
+
const requested = String(paramsOf(raw).scenarioId ?? "");
|
|
1236
|
+
const scores = requested ? SCENARIOS.filter(entry => entry.id === requested).map(entry => scoreScenario(entry, observed)) : scoreAll(observed);
|
|
1237
|
+
if (requested && !scores.length)
|
|
1238
|
+
throw new ContractError(`No benchmark scenario named ${requested}`);
|
|
1239
|
+
return result({ scores, passed: scores.every(entry => entry.passed) });
|
|
1240
|
+
}
|
|
1241
|
+
catch (error) {
|
|
1242
|
+
return failure(error);
|
|
1243
|
+
}
|
|
1244
|
+
},
|
|
1245
|
+
});
|
|
1246
|
+
pi.registerTool({
|
|
1247
|
+
name: "bizar_pr_shepherd", label: "Bizar PR Shepherd",
|
|
1248
|
+
description: "Track the pull-request lifecycle as a state machine: the exact SHA under review, observed checks and reviews, a bounded repair budget, and the next condition to wait for. Bizar does not talk to GitHub; report what the host's native tool saw.",
|
|
1249
|
+
parameters: pi.zod.object({
|
|
1250
|
+
action: pi.zod.enum(["status", "push", "checks", "reviews", "repair", "merge", "auxiliary"]),
|
|
1251
|
+
pullRequest: pi.zod.number().optional(),
|
|
1252
|
+
sha: pi.zod.string().optional(),
|
|
1253
|
+
mergeability: pi.zod.enum(["mergeable", "blocked", "conflicted", "unknown"]).optional(),
|
|
1254
|
+
checks: pi.zod.array(pi.zod.object({ name: pi.zod.string(), required: pi.zod.boolean(), state: pi.zod.enum(["pending", "passing", "failing"]) })).optional(),
|
|
1255
|
+
reviews: pi.zod.array(pi.zod.object({ id: pi.zod.string(), blocking: pi.zod.boolean(), resolved: pi.zod.boolean(), summary: pi.zod.string() })).optional(),
|
|
1256
|
+
note: pi.zod.string().optional(),
|
|
1257
|
+
}),
|
|
1258
|
+
async execute(_id, raw, _signal, _onUpdate, ctx) {
|
|
1259
|
+
try {
|
|
1260
|
+
const params = paramsOf(raw);
|
|
1261
|
+
const action = String(params.action);
|
|
1262
|
+
const active = currentBinding(ctx);
|
|
1263
|
+
const runId = active.journal.snapshot().run?.runId ?? "no-run";
|
|
1264
|
+
const store = shepherds.get(runId) ?? emptyShepherd(active.cwd, currentBinding(ctx).journal.snapshot().run?.objective ? "current" : "unknown");
|
|
1265
|
+
const save = (state, extra = {}) => { shepherds.set(runId, state); return result({ ...state, ...extra }); };
|
|
1266
|
+
if (action === "status")
|
|
1267
|
+
return save(store);
|
|
1268
|
+
if (action === "auxiliary")
|
|
1269
|
+
return save(store, { auxiliaryWork: auxiliaryWork(store) });
|
|
1270
|
+
if (action === "push")
|
|
1271
|
+
return save(recordPush(store, { sha: String(params.sha ?? ""), pullRequest: Number(params.pullRequest ?? 0), mergeability: params.mergeability }));
|
|
1272
|
+
if (action === "checks")
|
|
1273
|
+
return save(observeChecks(store, { sha: String(params.sha ?? ""), checks: Array.isArray(params.checks) ? params.checks : [], mergeability: params.mergeability }));
|
|
1274
|
+
if (action === "reviews")
|
|
1275
|
+
return save(observeReviews(store, Array.isArray(params.reviews) ? params.reviews : []));
|
|
1276
|
+
if (action === "repair")
|
|
1277
|
+
return save(recordShepherdRepair(store, { sha: String(params.sha ?? ""), note: String(params.note ?? "") }));
|
|
1278
|
+
return save(recordMerge(store));
|
|
941
1279
|
}
|
|
942
1280
|
catch (error) {
|
|
943
1281
|
return failure(error);
|
|
@@ -1012,7 +1350,7 @@ export default function bizarExtension(pi) {
|
|
|
1012
1350
|
else {
|
|
1013
1351
|
const active = currentBinding(ctx);
|
|
1014
1352
|
if (command === "run" || command === "init")
|
|
1015
|
-
value = runWorkflow(active, "start", { objective: objectiveOf(words.join(" ")) });
|
|
1353
|
+
value = runWorkflow(active, "start", { objective: completableObjective(objectiveOf(words.join(" ")), active.cwd) });
|
|
1016
1354
|
else if (command === "status" || command === "next")
|
|
1017
1355
|
value = runWorkflow(active, command === "next" ? "next" : "status", {});
|
|
1018
1356
|
else if (command === "cancel")
|
|
@@ -1021,8 +1359,12 @@ export default function bizarExtension(pi) {
|
|
|
1021
1359
|
value = listEvidence(active);
|
|
1022
1360
|
else if (command === "recipe")
|
|
1023
1361
|
value = analyzeRecipe(active.cwd, objectiveOf(words.join(" ")));
|
|
1024
|
-
else if (command === "inspect")
|
|
1025
|
-
|
|
1362
|
+
else if (command === "inspect") {
|
|
1363
|
+
const state = active.journal.snapshot();
|
|
1364
|
+
if (!state.run)
|
|
1365
|
+
throw new ContractError("No workflow run exists; start one first");
|
|
1366
|
+
value = projectWorkflow(state, admittedRequirements(state.run, state.run.workItems, workingTreeDigest(active.cwd)));
|
|
1367
|
+
}
|
|
1026
1368
|
else
|
|
1027
1369
|
throw new ContractError(`Unknown /bizar command: ${command}`);
|
|
1028
1370
|
}
|