@kontourai/flow-agents 0.1.2 → 0.3.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/.github/dependabot.yml +23 -0
- package/.github/workflows/release-please.yml +31 -0
- package/.github/workflows/runtime-compat.yml +118 -0
- package/CHANGELOG.md +46 -0
- package/CONTRIBUTING.md +4 -0
- package/README.md +80 -18
- package/build/src/cli/flow-kit.js +9 -4
- package/build/src/cli/init.js +215 -5
- package/build/src/cli/runtime-adapter.js +9 -5
- package/build/src/cli/telemetry-doctor.js +4 -1
- package/build/src/cli/utterance-check.js +65 -1
- package/build/src/runtime-adapters.js +34 -0
- package/build/src/tools/build-universal-bundles.js +285 -0
- package/build/src/tools/filter-installed-packs.js +3 -0
- package/build/src/tools/validate-source-tree.js +5 -1
- package/console.telemetry.json +115 -20
- package/context/scripts/telemetry/lib/config.sh +5 -1
- package/context/settings/flow-agents-settings.json +7 -0
- package/docs/_layouts/default.html +2 -0
- package/docs/context-map.md +1 -0
- package/docs/index.md +53 -4
- package/docs/integrations/conformance.md +246 -0
- package/docs/integrations/framework-adapter.md +275 -0
- package/docs/integrations/harness-install.md +213 -0
- package/docs/integrations/index.md +58 -0
- package/docs/integrations/knowledge-kit-live.md +211 -0
- package/docs/kit-authoring-guide.md +169 -0
- package/docs/north-star.md +2 -2
- package/docs/spec/runtime-hook-surface.md +525 -0
- package/docs/survey-utterance-check.md +211 -94
- package/docs/vision.md +45 -0
- package/evals/acceptance/run.sh +13 -2
- package/evals/acceptance/test_knowledge_kit_live.sh +221 -0
- package/evals/acceptance/test_opencode_harness.sh +121 -0
- package/evals/acceptance/test_pi_harness.sh +113 -0
- package/evals/integration/test_bundle_install.sh +226 -1
- package/evals/integration/test_bundle_lifecycle.sh +641 -0
- package/evals/integration/test_runtime_adapter_activation.sh +113 -1
- package/evals/integration/test_utterance_check.sh +291 -44
- package/evals/run.sh +2 -0
- package/evals/static/test_universal_bundles.sh +137 -2
- package/integrations/strands/README.md +256 -0
- package/integrations/strands/example.py +74 -0
- package/integrations/strands/examples/knowledge_kit_live.py +461 -0
- package/integrations/strands/flow_agents_strands/__init__.py +27 -0
- package/integrations/strands/flow_agents_strands/hooks.py +194 -0
- package/integrations/strands/flow_agents_strands/policy.py +348 -0
- package/integrations/strands/flow_agents_strands/steering.py +225 -0
- package/integrations/strands/flow_agents_strands/telemetry.py +238 -0
- package/integrations/strands/pyproject.toml +38 -0
- package/integrations/strands/tests/__init__.py +0 -0
- package/integrations/strands/tests/test_hooks.py +392 -0
- package/integrations/strands/tests/test_policy.py +315 -0
- package/integrations/strands/tests/test_telemetry.py +184 -0
- package/integrations/strands-ts/README.md +224 -0
- package/integrations/strands-ts/bin/conformance-shim.mjs +257 -0
- package/integrations/strands-ts/package.json +53 -0
- package/integrations/strands-ts/src/hooks.ts +312 -0
- package/integrations/strands-ts/src/index.ts +22 -0
- package/integrations/strands-ts/src/policy.ts +345 -0
- package/integrations/strands-ts/src/telemetry.ts +251 -0
- package/integrations/strands-ts/test/test-policy.ts +322 -0
- package/integrations/strands-ts/test/test-steering.ts +159 -0
- package/integrations/strands-ts/test/test-telemetry.ts +226 -0
- package/integrations/strands-ts/tsconfig.json +20 -0
- package/kits/catalog.json +6 -0
- package/kits/knowledge/adapters/default-store/index.js +821 -0
- package/kits/knowledge/adapters/flow-runner/index.js +1179 -0
- package/kits/knowledge/adapters/flow-runner/telemetry.js +174 -0
- package/kits/knowledge/docs/README.md +135 -0
- package/kits/knowledge/docs/store-contract.md +526 -0
- package/kits/knowledge/evals/consolidation/suite.test.js +1234 -0
- package/kits/knowledge/evals/contract-suite/suite.test.js +670 -0
- package/kits/knowledge/evals/ingest-compile/suite.test.js +574 -0
- package/kits/knowledge/evals/synthesis/suite.test.js +909 -0
- package/kits/knowledge/flows/compile.flow.json +60 -0
- package/kits/knowledge/flows/consolidate.flow.json +77 -0
- package/kits/knowledge/flows/ingest.flow.json +60 -0
- package/kits/knowledge/flows/store-contract.flow.json +48 -0
- package/kits/knowledge/flows/synthesize.flow.json +77 -0
- package/kits/knowledge/kit.json +78 -0
- package/package.json +7 -2
- package/packaging/conformance/README.md +142 -0
- package/packaging/conformance/fixtures/config-protection--allow-no-path.json +18 -0
- package/packaging/conformance/fixtures/config-protection--allow-safe-file.json +20 -0
- package/packaging/conformance/fixtures/config-protection--block-biome.json +20 -0
- package/packaging/conformance/fixtures/config-protection--block-eslintrc.json +20 -0
- package/packaging/conformance/fixtures/quality-gate--allow-no-path.json +17 -0
- package/packaging/conformance/fixtures/quality-gate--allow-nonexistent-file.json +19 -0
- package/packaging/conformance/fixtures/stop-goal-fit--allow-clean-cwd.json +17 -0
- package/packaging/conformance/fixtures/stop-goal-fit--block-strict-mode.json +23 -0
- package/packaging/conformance/fixtures/stop-goal-fit--warn-active-delivery.json +21 -0
- package/packaging/conformance/fixtures/workflow-steering--allow-no-state.json +16 -0
- package/packaging/conformance/fixtures/workflow-steering--inject-active-state.json +29 -0
- package/packaging/conformance/fixtures/workflow-steering--inject-subagent-steering.json +25 -0
- package/packaging/conformance/package.json +4 -0
- package/packaging/conformance/run-conformance.js +322 -0
- package/packaging/manifest.json +59 -0
- package/schemas/flow-agents-settings.schema.json +48 -0
- package/scripts/README.md +4 -0
- package/scripts/dogfood.js +16 -0
- package/scripts/hooks/opencode-hook-adapter.js +123 -0
- package/scripts/hooks/opencode-telemetry-hook.js +101 -0
- package/scripts/hooks/pi-hook-adapter.js +123 -0
- package/scripts/hooks/pi-telemetry-hook.js +105 -0
- package/scripts/hooks/run-hook.js +8 -0
- package/scripts/hooks/utterance-check.js +124 -22
- package/scripts/telemetry/lib/config.sh +5 -1
- package/src/cli/flow-kit.ts +10 -4
- package/src/cli/init.ts +219 -6
- package/src/cli/runtime-adapter.ts +10 -5
- package/src/cli/telemetry-doctor.ts +4 -1
- package/src/cli/utterance-check.ts +71 -1
- package/src/runtime-adapters.ts +35 -0
- package/src/tools/build-universal-bundles.ts +283 -0
- package/src/tools/filter-installed-packs.ts +3 -0
- package/src/tools/validate-source-tree.ts +5 -1
|
@@ -361,6 +361,287 @@ function buildCodex(agents) {
|
|
|
361
361
|
writeText(path.join(bundle, "install.sh"), installScript("Codex", "/path/to/workspace"));
|
|
362
362
|
fs.chmodSync(path.join(bundle, "install.sh"), 0o755);
|
|
363
363
|
}
|
|
364
|
+
function exportOpencodeAgent(spec) {
|
|
365
|
+
// Determine agent mode: orchestrator-like agents -> primary, others -> subagent
|
|
366
|
+
const primaryAgents = new Set(["dev"]);
|
|
367
|
+
const mode = primaryAgents.has(spec.name) ? "primary" : "subagent";
|
|
368
|
+
const prompt = appendExportNote(sanitizeText(spec.prompt, "opencode", "<bundle-root>"), "Kiro hook wiring and JSON-only runtime fields were omitted. If this agent mentions Kiro-specific scheduler or hook behavior, treat that as optional operational guidance rather than a hard dependency.");
|
|
369
|
+
const lines = ["---"];
|
|
370
|
+
lines.push(`description: ${String(spec.description ?? "").trim()}`);
|
|
371
|
+
lines.push(`mode: ${mode}`);
|
|
372
|
+
lines.push(`model: ${mapped("claude_model_map", spec.model)}`);
|
|
373
|
+
lines.push("---");
|
|
374
|
+
lines.push("");
|
|
375
|
+
lines.push(prompt);
|
|
376
|
+
return lines.join("\n");
|
|
377
|
+
}
|
|
378
|
+
function exportOpencodePlugin() {
|
|
379
|
+
// Generate the Flow Agents opencode plugin.
|
|
380
|
+
// opencode plugins are auto-loaded from .opencode/plugins/*.js at startup.
|
|
381
|
+
//
|
|
382
|
+
// NOTE: opencode has no direct user-prompt-submit hook. For prompt-submit
|
|
383
|
+
// workflow steering, we wire the steering command behind session.created
|
|
384
|
+
// (for session-start steering context) and tool.execute.before (for
|
|
385
|
+
// policy). This is the closest reasonable approximation — documented here
|
|
386
|
+
// as an honest gap matching the codex live-hook-influence caveat pattern.
|
|
387
|
+
//
|
|
388
|
+
// KNOWN GAP (verified 2026-06-11, opencode v1.16.2): session.created is NOT
|
|
389
|
+
// delivered to plugin event handlers in opencode (non-interactive) mode.
|
|
390
|
+
// The session IS created server-side but the event fires before the plugin
|
|
391
|
+
// hook dispatch loop is active. As a result, agentSpawn telemetry (session.start)
|
|
392
|
+
// is never emitted in run-mode sessions — only tool.invoke/tool.result appear.
|
|
393
|
+
// This is an opencode runtime limitation, not a bug in this plugin.
|
|
394
|
+
// Session.start telemetry carries L1 conformance but is unavailable in run mode.
|
|
395
|
+
// See docs/spec/runtime-hook-surface.md opencode mapping row for the full gap note.
|
|
396
|
+
return `/**
|
|
397
|
+
* Flow Agents opencode plugin.
|
|
398
|
+
*
|
|
399
|
+
* Auto-loaded from .opencode/plugins/flow-agents.js at opencode startup.
|
|
400
|
+
* Delegates policy and telemetry decisions to shared scripts in scripts/hooks/
|
|
401
|
+
* using the same payload contract as the claude/codex adapters.
|
|
402
|
+
*
|
|
403
|
+
* EVENT MAPPING NOTE: opencode has no direct user-prompt-submit hook.
|
|
404
|
+
* Workflow steering (workflow-steering.js) is wired to session.created
|
|
405
|
+
* (session-start context) and tool.execute.before (per-tool policy).
|
|
406
|
+
* This approximates the UserPromptSubmit behavior in other runtimes but
|
|
407
|
+
* cannot intercept mid-session user messages before they are processed.
|
|
408
|
+
* This is an accepted gap documented here analogously to the codex
|
|
409
|
+
* live-hook-influence caveat.
|
|
410
|
+
*
|
|
411
|
+
* KNOWN GAP: session.created is NOT delivered to plugin handlers in opencode
|
|
412
|
+
* run (non-interactive) mode (verified v1.16.2, 2026-06-11). agentSpawn
|
|
413
|
+
* telemetry (session.start) is therefore absent from run-mode sessions.
|
|
414
|
+
* tool.invoke and tool.result events (L1) are still recorded normally.
|
|
415
|
+
* This is an opencode runtime limitation; no workaround is available without
|
|
416
|
+
* a different hook surface. See docs/spec/runtime-hook-surface.md for details.
|
|
417
|
+
*/
|
|
418
|
+
|
|
419
|
+
import { spawnSync } from 'node:child_process';
|
|
420
|
+
import { join, basename } from 'node:path';
|
|
421
|
+
|
|
422
|
+
// opencode runs plugins inside its own compiled (Bun-based) binary, so
|
|
423
|
+
// process.execPath points at opencode itself — spawning it with a script
|
|
424
|
+
// path silently does nothing (caught by live acceptance smoke 2026-06-11).
|
|
425
|
+
// Resolve a real node binary instead; fall back to PATH lookup.
|
|
426
|
+
const NODE_BIN = basename(process.execPath).startsWith('node') ? process.execPath : 'node';
|
|
427
|
+
|
|
428
|
+
export const FlowAgentsPlugin = async ({ project, client, $, directory, worktree }) => {
|
|
429
|
+
const root = directory || process.cwd();
|
|
430
|
+
|
|
431
|
+
// The hook scripts read the event payload from stdin; an empty stdin makes
|
|
432
|
+
// the telemetry pipeline silently skip the emit (fail-open), so every spawn
|
|
433
|
+
// must pass a payload (caught by live acceptance smoke 2026-06-11).
|
|
434
|
+
function hookPayload(eventName, detail) {
|
|
435
|
+
return JSON.stringify({ hook_event_name: eventName, cwd: root, ...(detail || {}) });
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function runAdapter(adapterScript, eventName, detail, ...args) {
|
|
439
|
+
const adapterPath = join(root, 'scripts', 'hooks', adapterScript);
|
|
440
|
+
const result = spawnSync(NODE_BIN, [adapterPath, eventName, ...args], {
|
|
441
|
+
input: hookPayload(eventName, detail),
|
|
442
|
+
encoding: 'utf8',
|
|
443
|
+
cwd: root,
|
|
444
|
+
env: { ...process.env, FLOW_AGENTS_HOOK_RUNTIME: 'opencode' },
|
|
445
|
+
timeout: 30000,
|
|
446
|
+
});
|
|
447
|
+
try {
|
|
448
|
+
return JSON.parse(result.stdout || '{}');
|
|
449
|
+
} catch {
|
|
450
|
+
return { allow: true };
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function runTelemetry(eventName, detail) {
|
|
455
|
+
const telemetryPath = join(root, 'scripts', 'hooks', 'opencode-telemetry-hook.js');
|
|
456
|
+
spawnSync(NODE_BIN, [telemetryPath, eventName, 'dev'], {
|
|
457
|
+
input: hookPayload(eventName, detail),
|
|
458
|
+
encoding: 'utf8',
|
|
459
|
+
cwd: root,
|
|
460
|
+
env: { ...process.env, FLOW_AGENTS_TELEMETRY_RUNTIME: 'opencode' },
|
|
461
|
+
timeout: 10000,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
'session.created': async (_input, _output) => {
|
|
467
|
+
runTelemetry('session.created');
|
|
468
|
+
// Wire workflow steering on session start for context injection
|
|
469
|
+
runAdapter('opencode-hook-adapter.js', 'session.created', null, 'workflow-steering', 'workflow-steering.js', 'default');
|
|
470
|
+
},
|
|
471
|
+
'tool.execute.before': async (input, output) => {
|
|
472
|
+
const detail = { tool: input && input.tool, args: output && output.args };
|
|
473
|
+
runTelemetry('tool.execute.before', detail);
|
|
474
|
+
const policyResult = runAdapter('opencode-hook-adapter.js', 'tool.execute.before', detail, 'config-protection', 'config-protection.js', 'default');
|
|
475
|
+
if (policyResult && policyResult.allow === false) {
|
|
476
|
+
throw new Error(policyResult.reason || 'Blocked by Flow Agents hook policy.');
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
'tool.execute.after': async (input, output) => {
|
|
480
|
+
const detail = { tool: input && input.tool };
|
|
481
|
+
runTelemetry('tool.execute.after', detail);
|
|
482
|
+
runAdapter('opencode-hook-adapter.js', 'tool.execute.after', detail, 'quality-gate', 'quality-gate.js', 'default');
|
|
483
|
+
},
|
|
484
|
+
'session.idle': async (_input, _output) => {
|
|
485
|
+
runTelemetry('session.idle');
|
|
486
|
+
runAdapter('opencode-hook-adapter.js', 'session.idle', null, 'stop-goal-fit', 'stop-goal-fit.js', 'default');
|
|
487
|
+
},
|
|
488
|
+
'session.error': async (_input, _output) => {
|
|
489
|
+
runTelemetry('session.error');
|
|
490
|
+
},
|
|
491
|
+
'session.compacted': async (_input, _output) => {
|
|
492
|
+
runTelemetry('session.compacted');
|
|
493
|
+
},
|
|
494
|
+
'permission.asked': async (_input, _output) => {
|
|
495
|
+
runTelemetry('permission.asked');
|
|
496
|
+
},
|
|
497
|
+
'file.edited': async (_input, _output) => {
|
|
498
|
+
runTelemetry('file.edited');
|
|
499
|
+
},
|
|
500
|
+
};
|
|
501
|
+
};
|
|
502
|
+
`;
|
|
503
|
+
}
|
|
504
|
+
function exportOpencodeConfig() {
|
|
505
|
+
// opencode's config schema requires `instructions` to be an ARRAY of
|
|
506
|
+
// instruction file paths/globs (a bare string fails validation and aborts
|
|
507
|
+
// startup). AGENTS.md is loaded natively by opencode, so the config stays
|
|
508
|
+
// minimal rather than double-including it.
|
|
509
|
+
return `${JSON.stringify({
|
|
510
|
+
$schema: "https://opencode.ai/config.json",
|
|
511
|
+
}, null, 2)}\n`;
|
|
512
|
+
}
|
|
513
|
+
function buildOpencode(agents) {
|
|
514
|
+
const bundle = path.join(dist, "opencode");
|
|
515
|
+
resetDir(bundle);
|
|
516
|
+
copySharedContent(bundle, "opencode", "<bundle-root>");
|
|
517
|
+
writeText(path.join(bundle, manifest.opencode.task_dir, ".gitkeep"), "");
|
|
518
|
+
for (const spec of agents) {
|
|
519
|
+
writeText(path.join(bundle, ".opencode/agents", `${spec.name}.md`), exportOpencodeAgent(spec));
|
|
520
|
+
}
|
|
521
|
+
for (const skill of fs.readdirSync(path.join(root, "skills"))) {
|
|
522
|
+
const skillPath = path.join(root, "skills", skill, "SKILL.md");
|
|
523
|
+
if (fs.existsSync(skillPath))
|
|
524
|
+
writeText(path.join(bundle, ".opencode/skills", skill, "SKILL.md"), sanitizeText(readText(skillPath), "opencode", "<bundle-root>"));
|
|
525
|
+
}
|
|
526
|
+
writeText(path.join(bundle, ".opencode/plugins/flow-agents.js"), exportOpencodePlugin());
|
|
527
|
+
writeText(path.join(bundle, "opencode.json"), exportOpencodeConfig());
|
|
528
|
+
writeText(path.join(bundle, "AGENTS.md"), exportRootAgentsMd("opencode", agents, manifest.opencode.task_dir));
|
|
529
|
+
writeText(path.join(bundle, "README.md"), exportTargetReadme("opencode", "bash install.sh /path/to/workspace"));
|
|
530
|
+
writeText(path.join(bundle, "install.sh"), installScript("opencode", "/path/to/workspace"));
|
|
531
|
+
fs.chmodSync(path.join(bundle, "install.sh"), 0o755);
|
|
532
|
+
}
|
|
533
|
+
function exportPiExtension() {
|
|
534
|
+
// Generate the Flow Agents pi extension.
|
|
535
|
+
// pi extensions are auto-discovered from .pi/extensions/*.ts (needs project trust).
|
|
536
|
+
// pi has no named-subagent registry; agents are not exported. The extension
|
|
537
|
+
// provides workflow steering (via before_agent_start context injection),
|
|
538
|
+
// tool-call policy (via tool_call event), and telemetry delegation to shared scripts.
|
|
539
|
+
return `/**
|
|
540
|
+
* Flow Agents pi extension.
|
|
541
|
+
*
|
|
542
|
+
* Auto-discovered from .pi/extensions/flow-agents.ts at startup (needs project trust).
|
|
543
|
+
* Delegates policy and telemetry to shared scripts/hooks/ using spawnSync,
|
|
544
|
+
* mirroring the payload contract used by the claude/codex adapters.
|
|
545
|
+
*
|
|
546
|
+
* NOTE: pi has no named-subagent registry. Agents are not exported for pi.
|
|
547
|
+
* Rely on AGENTS.md + skills + this extension for workflow guidance.
|
|
548
|
+
*/
|
|
549
|
+
|
|
550
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
551
|
+
import { spawnSync } from "node:child_process";
|
|
552
|
+
import { join, basename } from "node:path";
|
|
553
|
+
|
|
554
|
+
// pi may run extensions under a non-node runtime (Bun), where process.execPath
|
|
555
|
+
// is not a node binary and spawning it with a script path silently fails.
|
|
556
|
+
// Same failure class the opencode live smoke caught on 2026-06-11.
|
|
557
|
+
const NODE_BIN = basename(process.execPath).startsWith("node") ? process.execPath : "node";
|
|
558
|
+
|
|
559
|
+
export default function (pi: ExtensionAPI) {
|
|
560
|
+
const root = process.cwd();
|
|
561
|
+
|
|
562
|
+
function runAdapter(adapterScript: string, eventName: string, hookId: string, relScript: string): { allow: boolean; context?: string; reason?: string } {
|
|
563
|
+
const adapterPath = join(root, "scripts", "hooks", adapterScript);
|
|
564
|
+
const payload = JSON.stringify({ hook_event_name: eventName, cwd: root });
|
|
565
|
+
const result = spawnSync(NODE_BIN, [adapterPath, eventName, hookId, relScript, "default"], {
|
|
566
|
+
input: payload,
|
|
567
|
+
encoding: "utf8",
|
|
568
|
+
cwd: root,
|
|
569
|
+
env: { ...process.env, FLOW_AGENTS_HOOK_RUNTIME: "pi" },
|
|
570
|
+
timeout: 30000,
|
|
571
|
+
});
|
|
572
|
+
try {
|
|
573
|
+
return JSON.parse(result.stdout || "{}") as { allow: boolean; context?: string; reason?: string };
|
|
574
|
+
} catch {
|
|
575
|
+
return { allow: true };
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function runTelemetry(eventName: string): void {
|
|
580
|
+
const telemetryPath = join(root, "scripts", "hooks", "pi-telemetry-hook.js");
|
|
581
|
+
const payload = JSON.stringify({ hook_event_name: eventName, cwd: root });
|
|
582
|
+
spawnSync(NODE_BIN, [telemetryPath, eventName, "dev"], {
|
|
583
|
+
input: payload,
|
|
584
|
+
encoding: "utf8",
|
|
585
|
+
cwd: root,
|
|
586
|
+
env: { ...process.env, FLOW_AGENTS_TELEMETRY_RUNTIME: "pi" },
|
|
587
|
+
timeout: 10000,
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
pi.on("session_start", async (_event, _ctx) => {
|
|
592
|
+
runTelemetry("session_start");
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
pi.on("before_agent_start", async (event, _ctx) => {
|
|
596
|
+
// Telemetry for agentSpawn is emitted by session_start above; do not repeat it here
|
|
597
|
+
// to avoid duplicate session.start events in the telemetry log.
|
|
598
|
+
// Inject workflow steering context at agent start
|
|
599
|
+
const result = runAdapter("pi-hook-adapter.js", "before_agent_start", "workflow-steering", "workflow-steering.js");
|
|
600
|
+
if (result.context) {
|
|
601
|
+
return {
|
|
602
|
+
systemPrompt: event.systemPrompt + "\\n\\n" + result.context,
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
pi.on("tool_call", async (event, _ctx) => {
|
|
608
|
+
runTelemetry("tool_call");
|
|
609
|
+
const result = runAdapter("pi-hook-adapter.js", "tool_call", "config-protection", "config-protection.js");
|
|
610
|
+
if (result && result.allow === false) {
|
|
611
|
+
return { block: true, reason: result.reason || "Blocked by Flow Agents hook policy." };
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
pi.on("tool_result", async (_event, _ctx) => {
|
|
616
|
+
runTelemetry("tool_result");
|
|
617
|
+
runAdapter("pi-hook-adapter.js", "tool_result", "quality-gate", "quality-gate.js");
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
pi.on("session_shutdown", async (_event, _ctx) => {
|
|
621
|
+
runTelemetry("session_shutdown");
|
|
622
|
+
runAdapter("pi-hook-adapter.js", "session_shutdown", "stop-goal-fit", "stop-goal-fit.js");
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
`;
|
|
626
|
+
}
|
|
627
|
+
function buildPi(agents) {
|
|
628
|
+
const bundle = path.join(dist, "pi");
|
|
629
|
+
resetDir(bundle);
|
|
630
|
+
copySharedContent(bundle, "pi", "<bundle-root>");
|
|
631
|
+
writeText(path.join(bundle, manifest.pi.task_dir, ".gitkeep"), "");
|
|
632
|
+
// pi has no named-subagent registry; agents are left canonical/unexported.
|
|
633
|
+
// Skills are exported to .pi/skills/ (direct .md files supported in that dir).
|
|
634
|
+
for (const skill of fs.readdirSync(path.join(root, "skills"))) {
|
|
635
|
+
const skillPath = path.join(root, "skills", skill, "SKILL.md");
|
|
636
|
+
if (fs.existsSync(skillPath))
|
|
637
|
+
writeText(path.join(bundle, ".pi/skills", skill, "SKILL.md"), sanitizeText(readText(skillPath), "pi", "<bundle-root>"));
|
|
638
|
+
}
|
|
639
|
+
writeText(path.join(bundle, ".pi/extensions/flow-agents.ts"), exportPiExtension());
|
|
640
|
+
writeText(path.join(bundle, "AGENTS.md"), exportRootAgentsMd("pi", agents, manifest.pi.task_dir));
|
|
641
|
+
writeText(path.join(bundle, "README.md"), exportTargetReadme("pi", "bash install.sh /path/to/workspace"));
|
|
642
|
+
writeText(path.join(bundle, "install.sh"), installScript("pi", "/path/to/workspace"));
|
|
643
|
+
fs.chmodSync(path.join(bundle, "install.sh"), 0o755);
|
|
644
|
+
}
|
|
364
645
|
function buildCatalog(agents) {
|
|
365
646
|
const kitsCatalog = path.join(root, "kits/catalog.json");
|
|
366
647
|
return {
|
|
@@ -379,6 +660,8 @@ export function main() {
|
|
|
379
660
|
buildKiro(agents);
|
|
380
661
|
buildClaudeCode(agents);
|
|
381
662
|
buildCodex(agents);
|
|
663
|
+
buildOpencode(agents);
|
|
664
|
+
buildPi(agents);
|
|
382
665
|
writeText(path.join(dist, "catalog.json"), `${JSON.stringify(buildCatalog(agents), null, 2)}\n`);
|
|
383
666
|
writeText(path.join(dist, "README.md"), "# Universal Bundles\n\nRun `npm run build:bundles` from the repo root to regenerate these bundles.\n");
|
|
384
667
|
console.log("Built bundles:");
|
|
@@ -386,6 +669,8 @@ export function main() {
|
|
|
386
669
|
console.log(" - dist/kiro");
|
|
387
670
|
console.log(" - dist/claude-code");
|
|
388
671
|
console.log(" - dist/codex");
|
|
672
|
+
console.log(" - dist/opencode");
|
|
673
|
+
console.log(" - dist/pi");
|
|
389
674
|
if (printDiagnostics && dropDiagnostics.length) {
|
|
390
675
|
console.error("Export sanitization diagnostics:");
|
|
391
676
|
for (const item of dropDiagnostics)
|
|
@@ -108,10 +108,13 @@ export function main(argv = process.argv.slice(2)) {
|
|
|
108
108
|
removed += pruneNamedDirs(rootDir, "skills", allPackMembers(packs, "skills"), selected.skills, dryRun);
|
|
109
109
|
removed += pruneNamedDirs(rootDir, ".claude/skills", allPackMembers(packs, "skills"), selected.skills, dryRun);
|
|
110
110
|
removed += pruneNamedDirs(rootDir, ".codex/skills", allPackMembers(packs, "skills"), selected.skills, dryRun);
|
|
111
|
+
removed += pruneNamedDirs(rootDir, ".opencode/skills", allPackMembers(packs, "skills"), selected.skills, dryRun);
|
|
112
|
+
removed += pruneNamedDirs(rootDir, ".pi/skills", allPackMembers(packs, "skills"), selected.skills, dryRun);
|
|
111
113
|
removed += pruneNamedDirs(rootDir, "powers", allPackMembers(packs, "powers"), selected.powers, dryRun);
|
|
112
114
|
removed += pruneAgentFiles(rootDir, "agents", ".json", allPackMembers(packs, "agents"), selected.agents, dryRun);
|
|
113
115
|
removed += pruneAgentFiles(rootDir, ".claude/agents", ".md", allPackMembers(packs, "agents"), selected.agents, dryRun);
|
|
114
116
|
removed += pruneAgentFiles(rootDir, ".codex/agents", ".toml", allPackMembers(packs, "agents"), selected.agents, dryRun);
|
|
117
|
+
removed += pruneAgentFiles(rootDir, ".opencode/agents", ".md", allPackMembers(packs, "agents"), selected.agents, dryRun);
|
|
115
118
|
const summary = {
|
|
116
119
|
selected_packs: [...selectedNames].sort(),
|
|
117
120
|
removed_entries: removed,
|
|
@@ -62,6 +62,10 @@ const hookFilePolicies = new Map([
|
|
|
62
62
|
["scripts/hooks/run-hook.js", { category: "hook runner", requiredNeedles: ["isHookEnabled", "Path traversal rejected"] }],
|
|
63
63
|
["scripts/hooks/config-protection.js", { category: "policy hook", requiredNeedles: ["Config Protection Hook"] }],
|
|
64
64
|
["scripts/hooks/governance-audit.sh", { category: "policy hook", requiredNeedles: ["governance-audit.sh", "audit_emit"] }],
|
|
65
|
+
["scripts/hooks/opencode-hook-adapter.js", { category: "runtime adapter", requiredNeedles: ["opencode", "run-hook.js"] }],
|
|
66
|
+
["scripts/hooks/opencode-telemetry-hook.js", { category: "telemetry shim", requiredNeedles: ["opencode", "telemetry"] }],
|
|
67
|
+
["scripts/hooks/pi-hook-adapter.js", { category: "runtime adapter", requiredNeedles: ["pi", "run-hook.js"] }],
|
|
68
|
+
["scripts/hooks/pi-telemetry-hook.js", { category: "telemetry shim", requiredNeedles: ["pi", "telemetry"] }],
|
|
65
69
|
["scripts/hooks/post-edit-accumulator.js", { category: "policy hook", requiredNeedles: ["Post-Edit"] }],
|
|
66
70
|
["scripts/hooks/pre-commit-quality.js", { category: "repo guardrail hook", requiredNeedles: ["staged"] }],
|
|
67
71
|
["scripts/hooks/quality-gate.js", { category: "policy hook", requiredNeedles: ["Quality"] }],
|
|
@@ -95,7 +99,7 @@ const requiredUsageFeedbackFiles = [
|
|
|
95
99
|
const fixtureOwnershipSelfAuditRefs = new Set([
|
|
96
100
|
"evals/integration/test_fixture_retirement_audit.sh",
|
|
97
101
|
]);
|
|
98
|
-
const pythonInventoryExcludes = new Set([".git", ".flow-agents", "node_modules", ".venv", "dist", "__pycache__", ".pytest_cache", ".cache", "build"]);
|
|
102
|
+
const pythonInventoryExcludes = new Set([".git", ".flow-agents", "node_modules", ".venv", "dist", "__pycache__", ".pytest_cache", ".cache", "build", "integrations"]);
|
|
99
103
|
const pythonCommandScanRoots = ["README.md", "docs", "context", "skills", "prompts", "agents", "evals", "scripts", "packaging", "package.json"];
|
|
100
104
|
const allowedPythonCommandFiles = [
|
|
101
105
|
/^agents\/tool-explore-deps\.json$/,
|
package/console.telemetry.json
CHANGED
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"id": "flow-agents-workflow-state",
|
|
10
10
|
"label": "Workflow state",
|
|
11
11
|
"root": "product:flow-agents:.flow-agents",
|
|
12
|
-
"files": [
|
|
12
|
+
"files": [
|
|
13
|
+
"state.json"
|
|
14
|
+
],
|
|
13
15
|
"attributes": {
|
|
14
16
|
"taskSlug": "task_slug",
|
|
15
17
|
"repo": "repo",
|
|
@@ -30,7 +32,9 @@
|
|
|
30
32
|
"id": "flow-agents-acceptance",
|
|
31
33
|
"label": "Acceptance criteria",
|
|
32
34
|
"root": "product:flow-agents:.flow-agents",
|
|
33
|
-
"files": [
|
|
35
|
+
"files": [
|
|
36
|
+
"acceptance.json"
|
|
37
|
+
],
|
|
34
38
|
"attributes": {
|
|
35
39
|
"taskSlug": "task_slug",
|
|
36
40
|
"repo": "repo",
|
|
@@ -47,7 +51,9 @@
|
|
|
47
51
|
"id": "flow-agents-evidence",
|
|
48
52
|
"label": "Verification evidence",
|
|
49
53
|
"root": "product:flow-agents:.flow-agents",
|
|
50
|
-
"files": [
|
|
54
|
+
"files": [
|
|
55
|
+
"evidence.json"
|
|
56
|
+
],
|
|
51
57
|
"attributes": {
|
|
52
58
|
"taskSlug": "task_slug",
|
|
53
59
|
"repo": "repo",
|
|
@@ -67,7 +73,9 @@
|
|
|
67
73
|
"id": "flow-agents-handoffs",
|
|
68
74
|
"label": "Handoffs",
|
|
69
75
|
"root": "product:flow-agents:.flow-agents",
|
|
70
|
-
"files": [
|
|
76
|
+
"files": [
|
|
77
|
+
"handoff.json"
|
|
78
|
+
],
|
|
71
79
|
"attributes": {
|
|
72
80
|
"taskSlug": "task_slug",
|
|
73
81
|
"repo": "repo",
|
|
@@ -87,7 +95,9 @@
|
|
|
87
95
|
"id": "flow-agents-learning",
|
|
88
96
|
"label": "Learning records",
|
|
89
97
|
"root": "product:flow-agents:.flow-agents",
|
|
90
|
-
"files": [
|
|
98
|
+
"files": [
|
|
99
|
+
"learning.json"
|
|
100
|
+
],
|
|
91
101
|
"attributes": {
|
|
92
102
|
"taskSlug": "task_slug",
|
|
93
103
|
"repo": "repo",
|
|
@@ -101,22 +111,75 @@
|
|
|
101
111
|
}
|
|
102
112
|
],
|
|
103
113
|
"facets": [
|
|
104
|
-
{
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
{
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
{
|
|
115
|
+
"id": "skills",
|
|
116
|
+
"label": "Skills",
|
|
117
|
+
"attribute": "skill",
|
|
118
|
+
"limit": 16
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"id": "tools",
|
|
122
|
+
"label": "Tools",
|
|
123
|
+
"attribute": "toolName",
|
|
124
|
+
"limit": 16
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"id": "flows",
|
|
128
|
+
"label": "Flows",
|
|
129
|
+
"attribute": "flow",
|
|
130
|
+
"limit": 16
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"id": "repositories",
|
|
134
|
+
"label": "Repositories",
|
|
135
|
+
"attribute": "repo",
|
|
136
|
+
"limit": 16
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"id": "projects",
|
|
140
|
+
"label": "Projects",
|
|
141
|
+
"attribute": "project",
|
|
142
|
+
"limit": 16
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"id": "runtimes",
|
|
146
|
+
"label": "Runtimes",
|
|
147
|
+
"attribute": "runtime",
|
|
148
|
+
"limit": 12
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"id": "agents",
|
|
152
|
+
"label": "Agents",
|
|
153
|
+
"attribute": "agent",
|
|
154
|
+
"limit": 16
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"id": "models",
|
|
158
|
+
"label": "Models",
|
|
159
|
+
"attribute": "model",
|
|
160
|
+
"limit": 16
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"id": "statuses",
|
|
164
|
+
"label": "Statuses",
|
|
165
|
+
"attribute": "status",
|
|
166
|
+
"limit": 12
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"id": "outcomes",
|
|
170
|
+
"label": "Outcomes",
|
|
171
|
+
"attribute": "outcome",
|
|
172
|
+
"limit": 12
|
|
173
|
+
}
|
|
114
174
|
],
|
|
115
175
|
"flows": [
|
|
116
176
|
{
|
|
117
177
|
"id": "builder.shape",
|
|
118
178
|
"label": "Builder shape",
|
|
119
|
-
"match": {
|
|
179
|
+
"match": {
|
|
180
|
+
"attribute": "flow",
|
|
181
|
+
"includes": "builder.shape"
|
|
182
|
+
},
|
|
120
183
|
"titleAttribute": "title",
|
|
121
184
|
"detailAttributes": {
|
|
122
185
|
"Project": "project",
|
|
@@ -135,7 +198,10 @@
|
|
|
135
198
|
{
|
|
136
199
|
"id": "builder.build",
|
|
137
200
|
"label": "Builder build",
|
|
138
|
-
"match": {
|
|
201
|
+
"match": {
|
|
202
|
+
"attribute": "flow",
|
|
203
|
+
"includes": "builder.build"
|
|
204
|
+
},
|
|
139
205
|
"titleAttribute": "title",
|
|
140
206
|
"detailAttributes": {
|
|
141
207
|
"Project": "project",
|
|
@@ -154,22 +220,51 @@
|
|
|
154
220
|
{
|
|
155
221
|
"id": "delivery",
|
|
156
222
|
"label": "Delivery workflows",
|
|
157
|
-
"match": {
|
|
223
|
+
"match": {
|
|
224
|
+
"attribute": "skill",
|
|
225
|
+
"includes": "deliver"
|
|
226
|
+
},
|
|
158
227
|
"titleAttribute": "title",
|
|
159
228
|
"limit": 10
|
|
160
229
|
},
|
|
161
230
|
{
|
|
162
231
|
"id": "verification",
|
|
163
232
|
"label": "Verification workflows",
|
|
164
|
-
"match": {
|
|
233
|
+
"match": {
|
|
234
|
+
"attribute": "skill",
|
|
235
|
+
"includes": "verify-work"
|
|
236
|
+
},
|
|
165
237
|
"titleAttribute": "title",
|
|
166
238
|
"limit": 10
|
|
167
239
|
},
|
|
168
240
|
{
|
|
169
241
|
"id": "review",
|
|
170
242
|
"label": "Review workflows",
|
|
171
|
-
"match": {
|
|
243
|
+
"match": {
|
|
244
|
+
"attribute": "skill",
|
|
245
|
+
"includes": "review-work"
|
|
246
|
+
},
|
|
247
|
+
"titleAttribute": "title",
|
|
248
|
+
"limit": 10
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"id": "knowledge",
|
|
252
|
+
"label": "Knowledge flows",
|
|
253
|
+
"match": {
|
|
254
|
+
"attribute": "flow",
|
|
255
|
+
"includes": "knowledge."
|
|
256
|
+
},
|
|
172
257
|
"titleAttribute": "title",
|
|
258
|
+
"detailAttributes": {
|
|
259
|
+
"Project": "project",
|
|
260
|
+
"Repository": "repo",
|
|
261
|
+
"Task": "taskSlug",
|
|
262
|
+
"Status": "status",
|
|
263
|
+
"Outcome": "outcome",
|
|
264
|
+
"Agent": "agent",
|
|
265
|
+
"Runtime": "runtime",
|
|
266
|
+
"Observed": "observedAt"
|
|
267
|
+
},
|
|
173
268
|
"limit": 10
|
|
174
269
|
}
|
|
175
270
|
]
|
|
@@ -6,7 +6,11 @@ TELEMETRY_CONFIG_FILE="${TELEMETRY_CONFIG_FILE:-${TELEMETRY_DIR}/telemetry.conf}
|
|
|
6
6
|
|
|
7
7
|
# Defaults
|
|
8
8
|
TELEMETRY_ENABLED="${TELEMETRY_ENABLED:-true}"
|
|
9
|
-
|
|
9
|
+
# TELEMETRY_DIR is <workspace>/scripts/telemetry, so the workspace root is
|
|
10
|
+
# two levels up. Three levels escaped into the workspace's PARENT directory
|
|
11
|
+
# (caught by live acceptance smoke 2026-06-11: events landed in /tmp/.telemetry
|
|
12
|
+
# instead of the installed workspace).
|
|
13
|
+
TELEMETRY_DATA_DIR="${TELEMETRY_DATA_DIR:-$(cd "${TELEMETRY_DIR}/../.." && pwd)/.telemetry}"
|
|
10
14
|
TELEMETRY_SESSION_DIR="${TELEMETRY_SESSION_DIR:-${TELEMETRY_DATA_DIR}/sessions}"
|
|
11
15
|
TELEMETRY_ENRICH_SYSTEM="${TELEMETRY_ENRICH_SYSTEM:-true}"
|
|
12
16
|
TELEMETRY_ENRICH_WORKSPACE="${TELEMETRY_ENRICH_WORKSPACE:-true}"
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
<a href="{{ '/workflow-usage-guide.html' | relative_url }}">Workflow Guide</a>
|
|
45
45
|
<a href="{{ '/agent-system-guidebook.html' | relative_url }}">System Guidebook</a>
|
|
46
46
|
<a href="{{ '/skills-map.html' | relative_url }}">Workflow Map</a>
|
|
47
|
+
<a href="{{ '/kit-authoring-guide.html' | relative_url }}">Kit Authoring Guide</a>
|
|
48
|
+
<a href="{{ '/flow-kit-repository-contract.html' | relative_url }}">Kit Contract</a>
|
|
47
49
|
<a href="{{ '/north-star.html' | relative_url }}">North Star</a>
|
|
48
50
|
<a href="{{ '/sandbox-policy.html' | relative_url }}">Execution Safety</a>
|
|
49
51
|
<a href="{{ '/veritas-integration.html' | relative_url }}">Governance Evidence</a>
|
package/docs/context-map.md
CHANGED
|
@@ -50,6 +50,7 @@ Machine-readable workflow state lives beside Markdown artifacts in `.flow-agents
|
|
|
50
50
|
| Schema | Title | ID |
|
|
51
51
|
| --- | --- | --- |
|
|
52
52
|
| backlog-provider-settings.schema.json | Flow Agents Backlog Provider Settings | https://flow-agents.dev/schemas/backlog-provider-settings.schema.json |
|
|
53
|
+
| flow-agents-settings.schema.json | Flow Agents Settings | https://flow-agents.dev/schemas/flow-agents-settings.schema.json |
|
|
53
54
|
| workflow-acceptance.schema.json | Flow Agents Workflow Acceptance | https://flow-agents.dev/schemas/workflow-acceptance.schema.json |
|
|
54
55
|
| workflow-critique.schema.json | Flow Agents Workflow Critique | https://flow-agents.dev/schemas/workflow-critique.schema.json |
|
|
55
56
|
| workflow-evidence.schema.json | Flow Agents Workflow Evidence | https://flow-agents.dev/schemas/workflow-evidence.schema.json |
|