@vibesdotdev/integrations-pi 0.0.0-stage → 1.0.0-rc.1
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/LICENSE +126 -0
- package/REFERENCE.md +284 -0
- package/dist/extension/active-tools.d.ts +17 -0
- package/dist/extension/active-tools.d.ts.map +1 -0
- package/dist/extension/active-tools.js +47 -0
- package/dist/extension/active-tools.js.map +1 -0
- package/dist/extension/approvals.d.ts +26 -0
- package/dist/extension/approvals.d.ts.map +1 -0
- package/dist/extension/approvals.js +124 -0
- package/dist/extension/approvals.js.map +1 -0
- package/dist/extension/boot.d.ts +18 -0
- package/dist/extension/boot.d.ts.map +1 -0
- package/dist/extension/boot.js +129 -0
- package/dist/extension/boot.js.map +1 -0
- package/dist/extension/chrome.d.ts +37 -0
- package/dist/extension/chrome.d.ts.map +1 -0
- package/dist/extension/chrome.js +160 -0
- package/dist/extension/chrome.js.map +1 -0
- package/dist/extension/commands.d.ts +41 -0
- package/dist/extension/commands.d.ts.map +1 -0
- package/dist/extension/commands.js +47 -0
- package/dist/extension/commands.js.map +1 -0
- package/dist/extension/entry.d.ts +9 -0
- package/dist/extension/entry.d.ts.map +1 -0
- package/dist/extension/entry.js +16 -0
- package/dist/extension/entry.js.map +1 -0
- package/dist/extension/events.d.ts +17 -0
- package/dist/extension/events.d.ts.map +1 -0
- package/dist/extension/events.js +42 -0
- package/dist/extension/events.js.map +1 -0
- package/dist/extension/index.d.ts +54 -0
- package/dist/extension/index.d.ts.map +1 -0
- package/dist/extension/index.js +547 -0
- package/dist/extension/index.js.map +1 -0
- package/dist/extension/models.d.ts +30 -0
- package/dist/extension/models.d.ts.map +1 -0
- package/dist/extension/models.js +104 -0
- package/dist/extension/models.js.map +1 -0
- package/dist/extension/render.d.ts +17 -0
- package/dist/extension/render.d.ts.map +1 -0
- package/dist/extension/render.js +107 -0
- package/dist/extension/render.js.map +1 -0
- package/dist/extension/runtime-host.d.ts +6 -0
- package/dist/extension/runtime-host.d.ts.map +1 -0
- package/dist/extension/runtime-host.js +2 -0
- package/dist/extension/runtime-host.js.map +1 -0
- package/dist/external/external.plugin.d.ts +7 -0
- package/dist/external/external.plugin.d.ts.map +1 -0
- package/dist/external/external.plugin.js +44 -0
- package/dist/external/external.plugin.js.map +1 -0
- package/dist/external/index.d.ts +8 -0
- package/dist/external/index.d.ts.map +1 -0
- package/dist/external/index.js +8 -0
- package/dist/external/index.js.map +1 -0
- package/dist/external/pi/bootstrap.d.ts +34 -0
- package/dist/external/pi/bootstrap.d.ts.map +1 -0
- package/dist/external/pi/bootstrap.js +238 -0
- package/dist/external/pi/bootstrap.js.map +1 -0
- package/dist/external/pi/guide.d.ts +3 -0
- package/dist/external/pi/guide.d.ts.map +1 -0
- package/dist/external/pi/guide.js +33 -0
- package/dist/external/pi/guide.js.map +1 -0
- package/dist/external/pi/index.d.ts +162 -0
- package/dist/external/pi/index.d.ts.map +1 -0
- package/dist/external/pi/index.js +7 -0
- package/dist/external/pi/index.js.map +1 -0
- package/dist/external/pi/installer.d.ts +19 -0
- package/dist/external/pi/installer.d.ts.map +1 -0
- package/dist/external/pi/installer.descriptor.d.ts +373 -0
- package/dist/external/pi/installer.descriptor.d.ts.map +1 -0
- package/dist/external/pi/installer.descriptor.js +43 -0
- package/dist/external/pi/installer.descriptor.js.map +1 -0
- package/dist/external/pi/installer.js +82 -0
- package/dist/external/pi/installer.js.map +1 -0
- package/dist/external/pi/installers/errors.d.ts +16 -0
- package/dist/external/pi/installers/errors.d.ts.map +1 -0
- package/dist/external/pi/installers/errors.js +15 -0
- package/dist/external/pi/installers/errors.js.map +1 -0
- package/dist/external/pi/installers/extension.d.ts +16 -0
- package/dist/external/pi/installers/extension.d.ts.map +1 -0
- package/dist/external/pi/installers/extension.js +292 -0
- package/dist/external/pi/installers/extension.js.map +1 -0
- package/dist/external/pi/installers/plan.d.ts +29 -0
- package/dist/external/pi/installers/plan.d.ts.map +1 -0
- package/dist/external/pi/installers/plan.js +391 -0
- package/dist/external/pi/installers/plan.js.map +1 -0
- package/dist/external/pi/installers/transaction.d.ts +5 -0
- package/dist/external/pi/installers/transaction.d.ts.map +1 -0
- package/dist/external/pi/installers/transaction.js +66 -0
- package/dist/external/pi/installers/transaction.js.map +1 -0
- package/dist/external/pi/manifest.d.ts +6 -0
- package/dist/external/pi/manifest.d.ts.map +1 -0
- package/dist/external/pi/manifest.js +91 -0
- package/dist/external/pi/manifest.js.map +1 -0
- package/dist/external/pi-presets.d.ts +817 -0
- package/dist/external/pi-presets.d.ts.map +1 -0
- package/dist/external/pi-presets.js +75 -0
- package/dist/external/pi-presets.js.map +1 -0
- package/dist/harness/constants.d.ts +46 -0
- package/dist/harness/constants.d.ts.map +1 -0
- package/dist/harness/constants.js +52 -0
- package/dist/harness/constants.js.map +1 -0
- package/dist/harness/identity.d.ts +29 -0
- package/dist/harness/identity.d.ts.map +1 -0
- package/dist/harness/identity.js +40 -0
- package/dist/harness/identity.js.map +1 -0
- package/dist/harness/index.d.ts +19 -0
- package/dist/harness/index.d.ts.map +1 -0
- package/dist/harness/index.js +18 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/harness/managed-block.d.ts +20 -0
- package/dist/harness/managed-block.d.ts.map +1 -0
- package/dist/harness/managed-block.js +23 -0
- package/dist/harness/managed-block.js.map +1 -0
- package/dist/harness/pi-naming.d.ts +32 -0
- package/dist/harness/pi-naming.d.ts.map +1 -0
- package/dist/harness/pi-naming.js +110 -0
- package/dist/harness/pi-naming.js.map +1 -0
- package/dist/harness/preflight.d.ts +5 -0
- package/dist/harness/preflight.d.ts.map +1 -0
- package/dist/harness/preflight.js +163 -0
- package/dist/harness/preflight.js.map +1 -0
- package/dist/harness/prompt.d.ts +14 -0
- package/dist/harness/prompt.d.ts.map +1 -0
- package/dist/harness/prompt.js +15 -0
- package/dist/harness/prompt.js.map +1 -0
- package/dist/harness/runtime.d.ts +137 -0
- package/dist/harness/runtime.d.ts.map +1 -0
- package/dist/harness/runtime.js +53 -0
- package/dist/harness/runtime.js.map +1 -0
- package/dist/harness/scope.d.ts +11 -0
- package/dist/harness/scope.d.ts.map +1 -0
- package/dist/harness/scope.js +47 -0
- package/dist/harness/scope.js.map +1 -0
- package/dist/harness/themes.d.ts +30 -0
- package/dist/harness/themes.d.ts.map +1 -0
- package/dist/harness/themes.js +225 -0
- package/dist/harness/themes.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/extension-api.d.ts +102 -0
- package/dist/schemas/extension-api.d.ts.map +1 -0
- package/dist/schemas/extension-api.js +2 -0
- package/dist/schemas/extension-api.js.map +1 -0
- package/dist/schemas/extension-host.d.ts +11 -0
- package/dist/schemas/extension-host.d.ts.map +1 -0
- package/dist/schemas/extension-host.js +12 -0
- package/dist/schemas/extension-host.js.map +1 -0
- package/dist/schemas/extension.d.ts +212 -0
- package/dist/schemas/extension.d.ts.map +1 -0
- package/dist/schemas/extension.js +93 -0
- package/dist/schemas/extension.js.map +1 -0
- package/dist/schemas/installer.d.ts +872 -0
- package/dist/schemas/installer.d.ts.map +1 -0
- package/dist/schemas/installer.js +138 -0
- package/dist/schemas/installer.js.map +1 -0
- package/dist/themes/vibes-dark.json +63 -0
- package/dist/themes/vibes-high-contrast.json +63 -0
- package/dist/themes/vibes-light.json +63 -0
- package/package.json +110 -4
- package/README.md +0 -3
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vibesdotdev/integrations-pi/extension/bun
|
|
3
|
+
*
|
|
4
|
+
* Pi extension factory. Duck-types ExtensionAPI so this file stays importable
|
|
5
|
+
* without @earendil-works/pi-coding-agent.
|
|
6
|
+
*
|
|
7
|
+
* Pi uses the Vibes runtime exactly as the CLI does, in-process: at
|
|
8
|
+
* session_start, with Pi's actual workspace, the extension boots one runtime
|
|
9
|
+
* (the CLI app on the Node host environment, `./runtime-host.ts`) that finds
|
|
10
|
+
* the workspace's vibes config and composes its plugins. The catalog is that
|
|
11
|
+
* runtime's `tools/tool` registry; tools run through the same caller path as
|
|
12
|
+
* `vibes tools run`; slash commands and the session bind run as CLI commands
|
|
13
|
+
* in that runtime. There is no daemon socket and no CLI child.
|
|
14
|
+
*
|
|
15
|
+
* No tool list lives here. Registration is all-or-nothing: the whole
|
|
16
|
+
* projection is validated before the first registerTool call, so a name
|
|
17
|
+
* collision cannot leave Pi with half a harness. The system prompt is
|
|
18
|
+
* generated from the names actually registered, which are the underscore
|
|
19
|
+
* projections - naming the registry ids here would point the model at tools
|
|
20
|
+
* that do not exist in Pi.
|
|
21
|
+
*/
|
|
22
|
+
import { type LiveToolDescriptor, type NormalizedLiveTool } from '@vibesdotdev/external/consumer/node/harness';
|
|
23
|
+
import { PI_BUILTIN_TOOLS, toPiToolName } from '../harness/pi-naming.ts';
|
|
24
|
+
import { type PiToolPolicyProfile } from '../harness/constants.ts';
|
|
25
|
+
export { PI_BUILTIN_TOOLS, toPiToolName };
|
|
26
|
+
export { buildLivePiHarnessPrompt } from '../harness/prompt.ts';
|
|
27
|
+
export { PiCatalogError } from '../harness/pi-naming.ts';
|
|
28
|
+
export { bootPiRuntime, resolvePiHostRoot, type PiRuntimeHost } from './runtime-host.ts';
|
|
29
|
+
export type { LiveToolDescriptor, NormalizedLiveTool };
|
|
30
|
+
/** Deprecated alias: the canonical type is the shared live descriptor. */
|
|
31
|
+
export type VibesHarnessTool = LiveToolDescriptor;
|
|
32
|
+
/** Deprecated alias: the canonical type is host-neutral. */
|
|
33
|
+
export type VibesOpencodeTool = LiveToolDescriptor;
|
|
34
|
+
import type { PiExtensionAPI, CreatePiExtensionOptions } from '../schemas/extension-api.ts';
|
|
35
|
+
export type { PiRuntimeHostOptions } from './runtime-host.ts';
|
|
36
|
+
export type { PiTypeField, PiExtensionAPI, PiToolResult, CreatePiExtensionOptions } from '../schemas/extension-api.ts';
|
|
37
|
+
export declare function createVibesPiExtension(options: CreatePiExtensionOptions): (pi: PiExtensionAPI) => Promise<{
|
|
38
|
+
/** Joins the current activation without blocking the native session_start callback. */
|
|
39
|
+
whenReady: () => Promise<void>;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* settings.json projection for a tool policy. `defaultTools` is omitted for
|
|
43
|
+
* `native`, which leaves the host default untouched; Pi keeps extension tools
|
|
44
|
+
* either way.
|
|
45
|
+
*/
|
|
46
|
+
export declare function piSettingsForProfile(profile?: PiToolPolicyProfile, extensionRef?: string): Record<string, import("@vibesdotdev/kernel/bootstrap").JsonValue | undefined>;
|
|
47
|
+
/** Default projection used by the installer: builtins off, extension loaded. */
|
|
48
|
+
export declare const VIBES_PI_SETTINGS: Record<string, import("@vibesdotdev/kernel/bootstrap").JsonValue | undefined>;
|
|
49
|
+
/**
|
|
50
|
+
* Default factory Pi loads from `.pi/extensions/vibes.ts`. This module is the
|
|
51
|
+
* host entry inside the Pi process, so it hands in Pi's own environment.
|
|
52
|
+
*/
|
|
53
|
+
export { default } from './entry.ts';
|
|
54
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extension/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA6BH,OAAO,EAKN,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EACN,gBAAgB,EAEhB,YAAY,EAEZ,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAGN,KAAK,mBAAmB,EACxB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC;AAC1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACzF,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAC;AACvD,0EAA0E;AAC1E,MAAM,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAClD,4DAA4D;AAC5D,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAEnD,OAAO,KAAK,EAAE,cAAc,EAAgB,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC1G,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAoBvH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,wBAAwB,IAKhC,IAAI,cAAc;IAibvD,uFAAuF;qBAClE,OAAO,CAAC,IAAI,CAAC;GAGpC;AASD;;;;GAIG;AACH,wBAAgB,oBAAoB,CACnC,OAAO,GAAE,mBAAoD,EAC7D,YAAY,SAA0B,iFAMtC;AAED,gFAAgF;AAChF,eAAO,MAAM,iBAAiB,+EAAmC,CAAC;AAElE;;;GAGG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vibesdotdev/integrations-pi/extension/bun
|
|
3
|
+
*
|
|
4
|
+
* Pi extension factory. Duck-types ExtensionAPI so this file stays importable
|
|
5
|
+
* without @earendil-works/pi-coding-agent.
|
|
6
|
+
*
|
|
7
|
+
* Pi uses the Vibes runtime exactly as the CLI does, in-process: at
|
|
8
|
+
* session_start, with Pi's actual workspace, the extension boots one runtime
|
|
9
|
+
* (the CLI app on the Node host environment, `./runtime-host.ts`) that finds
|
|
10
|
+
* the workspace's vibes config and composes its plugins. The catalog is that
|
|
11
|
+
* runtime's `tools/tool` registry; tools run through the same caller path as
|
|
12
|
+
* `vibes tools run`; slash commands and the session bind run as CLI commands
|
|
13
|
+
* in that runtime. There is no daemon socket and no CLI child.
|
|
14
|
+
*
|
|
15
|
+
* No tool list lives here. Registration is all-or-nothing: the whole
|
|
16
|
+
* projection is validated before the first registerTool call, so a name
|
|
17
|
+
* collision cannot leave Pi with half a harness. The system prompt is
|
|
18
|
+
* generated from the names actually registered, which are the underscore
|
|
19
|
+
* projections - naming the registry ids here would point the model at tools
|
|
20
|
+
* that do not exist in Pi.
|
|
21
|
+
*/
|
|
22
|
+
import { getRuntimePath } from '@vibesdotdev/paths';
|
|
23
|
+
import { getLogger } from '@vibesdotdev/logging';
|
|
24
|
+
import { PiExtensionEventSchema, PiStringSchema, PiHostContextSchema, PiToolCallEventSchema, PiJsonSchema, PiKernelInputSchema } from "../schemas/extension.js";
|
|
25
|
+
import { PiSettingsSchema } from "../schemas/installer.js";
|
|
26
|
+
import { parseOutput, renderCall, renderResult } from "./render.js";
|
|
27
|
+
import { projectVirtualModels } from "./models.js";
|
|
28
|
+
import { renderAgentRolePrompt } from '@vibesdotdev/external/harness';
|
|
29
|
+
import { forwardRuntimeEvents } from "./events.js";
|
|
30
|
+
import { applyVibesPiChrome, bindVibesSession, confirmMutatingTool } from "./chrome.js";
|
|
31
|
+
import { getPiApprovalMemory, approvalPosture } from "./approvals.js";
|
|
32
|
+
import { runVibesPiCommand, piCommandSpecs } from "./commands.js";
|
|
33
|
+
import { activatePiSearchHits, PI_INITIAL_TOOL_IDS, restrictPiToolsToDiscovery } from "./active-tools.js";
|
|
34
|
+
import { CapabilityError, CaughtFailureSchema, valueFreeFailure, settle, settleNow } from '@vibesdotdev/kernel';
|
|
35
|
+
import { bootPiRuntime } from "./runtime-host.js";
|
|
36
|
+
import { LiveToolDescriptorSchema, HostToolInputSchema, normalizeLiveTool } from '@vibesdotdev/external/consumer/node/harness';
|
|
37
|
+
import { PI_BUILTIN_TOOLS, assertPiToolCatalog, toPiToolName } from "../harness/pi-naming.js";
|
|
38
|
+
import { buildLivePiHarnessPrompt } from "../harness/prompt.js";
|
|
39
|
+
import { declarePiAgentIdentity, PI_HARNESS_ID } from "../harness/identity.js";
|
|
40
|
+
import { DEFAULT_PI_TOOL_POLICY_PROFILE, piDefaultToolsForProfile } from "../harness/constants.js";
|
|
41
|
+
export { PI_BUILTIN_TOOLS, toPiToolName };
|
|
42
|
+
export { buildLivePiHarnessPrompt } from "../harness/prompt.js";
|
|
43
|
+
export { PiCatalogError } from "../harness/pi-naming.js";
|
|
44
|
+
export { bootPiRuntime, resolvePiHostRoot } from "./runtime-host.js";
|
|
45
|
+
/** A tool's answer as Pi's text; an answer with nothing in it says so, as the CLI does. */
|
|
46
|
+
function toolText(output) {
|
|
47
|
+
if (output === undefined || output === '')
|
|
48
|
+
return '(no output)';
|
|
49
|
+
return PiStringSchema.safeParse(output).data ?? JSON.stringify(output, null, 2);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The model's tool arguments as the host boundary accepts them.
|
|
53
|
+
*
|
|
54
|
+
* Pi hands over whatever the model produced, so the JSON is decoded here rather
|
|
55
|
+
* than forwarded as `unknown`. An argument that is not JSON is the tool's own
|
|
56
|
+
* validation to reject with its message; this only decides what a tool call
|
|
57
|
+
* carries.
|
|
58
|
+
*/
|
|
59
|
+
function hostToolInput(params) {
|
|
60
|
+
return HostToolInputSchema.parse(params ?? {});
|
|
61
|
+
}
|
|
62
|
+
export function createVibesPiExtension(options) {
|
|
63
|
+
const reserved = options.reservedNames ?? PI_BUILTIN_TOOLS;
|
|
64
|
+
const injectSystemPrompt = options.injectSystemPrompt !== false;
|
|
65
|
+
const bootRuntime = options.bootRuntime ?? bootPiRuntime;
|
|
66
|
+
return async function vibesPiExtension(pi) {
|
|
67
|
+
// Name the Pi body before its runtime boots. Every agent-scoped organ
|
|
68
|
+
// (brain, presence, memories) stores per body and refuses an unnamed
|
|
69
|
+
// caller. An explicit VIBES_AGENT_ID still wins: see ../harness/identity.ts.
|
|
70
|
+
declarePiAgentIdentity(options.hostEnv);
|
|
71
|
+
let current;
|
|
72
|
+
let pendingHost;
|
|
73
|
+
// The activation session_start began. Pi starts a session at every startup, so a slash command
|
|
74
|
+
// typed before that, or while the runtime boots, runs once the activation lands.
|
|
75
|
+
let activation;
|
|
76
|
+
// Held in an object: resolving with the activation itself would adopt (and so raise) its failure.
|
|
77
|
+
let announceActivation = () => { };
|
|
78
|
+
const firstActivation = new Promise((resolve) => { announceActivation = resolve; });
|
|
79
|
+
// Who calls: this harness and its own conversation. Caller admission keys
|
|
80
|
+
// the seat (instance, participant, session) on them, so a call names its
|
|
81
|
+
// conversation and never pins a session: a `session init` the model makes
|
|
82
|
+
// moves the seat, and the next call follows it.
|
|
83
|
+
const harnessOf = (ctx) => ({
|
|
84
|
+
harness: options.hostEnv?.VIBES_HARNESS?.trim() || PI_HARNESS_ID,
|
|
85
|
+
harnessSessionId: ctx?.sessionManager?.getSessionId()
|
|
86
|
+
});
|
|
87
|
+
const bindSession = (selected, ctx) => {
|
|
88
|
+
selected.binding ??= (async () => {
|
|
89
|
+
const bound = await bindVibesSession(selected.host, ctx?.ui, harnessOf(ctx), selected.sessionId);
|
|
90
|
+
if (current === selected)
|
|
91
|
+
selected.sessionId = bound;
|
|
92
|
+
return bound;
|
|
93
|
+
})();
|
|
94
|
+
return selected.binding;
|
|
95
|
+
};
|
|
96
|
+
const posture = options.approvalPosture ?? approvalPosture(options.hostEnv);
|
|
97
|
+
let generation = 0;
|
|
98
|
+
let disposed = false;
|
|
99
|
+
/** Stops the runtime event forwarders the live activation installed. */
|
|
100
|
+
let stopEventForwarding = [];
|
|
101
|
+
const ownedNames = new Set();
|
|
102
|
+
/** Unregisters the virtual models the live activation projected. */
|
|
103
|
+
let unprojectModels = () => { };
|
|
104
|
+
const sessionFor = (ctx) => {
|
|
105
|
+
if (!current || disposed)
|
|
106
|
+
throw new CapabilityError('Vibes session workspace is not ready', 'UNAVAILABLE', { reason: 'pi-session-not-ready' });
|
|
107
|
+
if (ctx?.cwd && current.cwd !== current.paths.resolve(ctx.cwd))
|
|
108
|
+
throw new CapabilityError('Vibes tool context does not match the current session workspace', 'CONFLICT', { reason: 'pi-workspace-mismatch' });
|
|
109
|
+
return current;
|
|
110
|
+
};
|
|
111
|
+
const deactivateOwned = () => {
|
|
112
|
+
if (ownedNames.size > 0 && pi.getActiveTools && pi.setActiveTools)
|
|
113
|
+
pi.setActiveTools(pi.getActiveTools().filter((name) => !ownedNames.has(name)));
|
|
114
|
+
unprojectModels();
|
|
115
|
+
unprojectModels = () => { };
|
|
116
|
+
for (const stop of stopEventForwarding)
|
|
117
|
+
stop();
|
|
118
|
+
stopEventForwarding = [];
|
|
119
|
+
};
|
|
120
|
+
const activate = async (cwd, initialCatalog) => {
|
|
121
|
+
const priorActivation = activation;
|
|
122
|
+
const revision = ++generation;
|
|
123
|
+
const workspaceRoot = cwd;
|
|
124
|
+
// Same workspace, same runtime: keep the host Pi already booted.
|
|
125
|
+
// Pi fires session_start at every startup even when the workspace
|
|
126
|
+
// has not changed; re-booting costs ~16 seconds for nothing.
|
|
127
|
+
if (workspaceRoot && current?.cwd === workspaceRoot && !disposed && generation === revision) {
|
|
128
|
+
register(current.entries, current.toolByName);
|
|
129
|
+
return current;
|
|
130
|
+
}
|
|
131
|
+
const previous_ = current;
|
|
132
|
+
current = undefined;
|
|
133
|
+
deactivateOwned();
|
|
134
|
+
const previousPending = pendingHost;
|
|
135
|
+
pendingHost = undefined;
|
|
136
|
+
await previous_?.host.dispose();
|
|
137
|
+
if (priorActivation) {
|
|
138
|
+
const settled = await settle(() => priorActivation);
|
|
139
|
+
if (!settled.ok)
|
|
140
|
+
reportFailure('Previous Pi activation failed', settled.cause, {});
|
|
141
|
+
}
|
|
142
|
+
if (previousPending) {
|
|
143
|
+
const pending = await settle(() => previousPending);
|
|
144
|
+
if (pending.ok)
|
|
145
|
+
await pending.value.dispose();
|
|
146
|
+
else
|
|
147
|
+
reportFailure('Previous Pi boot failed', pending.cause, {});
|
|
148
|
+
}
|
|
149
|
+
if (disposed || generation !== revision)
|
|
150
|
+
return;
|
|
151
|
+
if (!workspaceRoot)
|
|
152
|
+
throw new CapabilityError('Pi host did not provide a session workspace directory', 'INVALID_INPUT', { reason: 'pi-workspace-required' });
|
|
153
|
+
const booting = bootRuntime(options.hostRoot === undefined ? { cwd: workspaceRoot, hostEnv: options.hostEnv } : { cwd: workspaceRoot, hostEnv: options.hostEnv, hostRoot: options.hostRoot });
|
|
154
|
+
pendingHost = booting;
|
|
155
|
+
let host;
|
|
156
|
+
try {
|
|
157
|
+
host = await booting;
|
|
158
|
+
}
|
|
159
|
+
catch (cause) {
|
|
160
|
+
if (pendingHost === booting)
|
|
161
|
+
pendingHost = undefined;
|
|
162
|
+
throw new CapabilityError('Pi runtime activation failed.', 'UNAVAILABLE', { reason: 'pi-activation-failed', cause });
|
|
163
|
+
}
|
|
164
|
+
if (pendingHost === booting)
|
|
165
|
+
pendingHost = undefined;
|
|
166
|
+
if (disposed || generation !== revision) {
|
|
167
|
+
await host.dispose();
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
// A catalog this host cannot project fails the activation, and the host it booted goes with it.
|
|
171
|
+
const projected = settleNow(() => {
|
|
172
|
+
const normalized = (initialCatalog ?? host.catalog()).map((descriptor) => normalizeLiveTool(LiveToolDescriptorSchema.parse(descriptor)));
|
|
173
|
+
return { entries: assertEntries(normalized, reserved), toolByName: new Map(normalized.map((tool) => [toPiToolName(tool.id), tool])) };
|
|
174
|
+
});
|
|
175
|
+
if (!projected.ok) {
|
|
176
|
+
await host.dispose();
|
|
177
|
+
throw new CapabilityError('Pi tool activation failed.', 'UNAVAILABLE', { reason: 'pi-activation-failed', cause: projected.cause });
|
|
178
|
+
}
|
|
179
|
+
const { entries, toolByName } = projected.value;
|
|
180
|
+
const paths = await getRuntimePath(host.runtime);
|
|
181
|
+
if (disposed || generation !== revision) {
|
|
182
|
+
await host.dispose();
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
current = {
|
|
186
|
+
paths,
|
|
187
|
+
cwd: paths.resolve(workspaceRoot),
|
|
188
|
+
sessionId: options.hostEnv?.VIBES_SESSION_ID?.trim() || undefined,
|
|
189
|
+
host,
|
|
190
|
+
entries,
|
|
191
|
+
toolByName,
|
|
192
|
+
idToName: new Map(entries.map((entry) => [entry.id, entry.name]))
|
|
193
|
+
};
|
|
194
|
+
try {
|
|
195
|
+
register(entries, toolByName);
|
|
196
|
+
}
|
|
197
|
+
catch (cause) {
|
|
198
|
+
current = undefined;
|
|
199
|
+
deactivateOwned();
|
|
200
|
+
await host.dispose();
|
|
201
|
+
throw new CapabilityError('Pi tool registration failed.', 'UNAVAILABLE', { reason: 'pi-activation-failed', cause });
|
|
202
|
+
}
|
|
203
|
+
return current;
|
|
204
|
+
};
|
|
205
|
+
const register = (entries, toolByName) => {
|
|
206
|
+
const discoveryToolIds = new Set(PI_INITIAL_TOOL_IDS);
|
|
207
|
+
const registeredSession = current;
|
|
208
|
+
for (const entry of entries) {
|
|
209
|
+
const def = toolByName.get(entry.name);
|
|
210
|
+
if (!def)
|
|
211
|
+
throw new CapabilityError('Pi tool projection is incomplete.', 'INTERNAL', { reason: 'pi-tool-projection-invalid' });
|
|
212
|
+
ownedNames.add(entry.name);
|
|
213
|
+
const isDiscovery = discoveryToolIds.has(entry.id);
|
|
214
|
+
pi.registerTool({
|
|
215
|
+
name: entry.name,
|
|
216
|
+
label: def.label,
|
|
217
|
+
description: def.description,
|
|
218
|
+
parameters: def.inputSchema,
|
|
219
|
+
// The runtime's published output schema, when it has one, so the
|
|
220
|
+
// model knows what a successful call answers before it makes one.
|
|
221
|
+
outputSchema: def.outputSchema,
|
|
222
|
+
exposure: isDiscovery ? 'direct' : 'codemode',
|
|
223
|
+
defaultActive: isDiscovery,
|
|
224
|
+
renderCall: (args, theme, context) => renderCall(def, args, theme, context),
|
|
225
|
+
renderResult,
|
|
226
|
+
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
227
|
+
const selected = sessionFor(ctx);
|
|
228
|
+
if (selected.toolByName.get(entry.name) !== def)
|
|
229
|
+
throw new CapabilityError('Vibes tool is unavailable in the current session', 'UNAVAILABLE', { reason: 'pi-stale-tool' });
|
|
230
|
+
if (!selected.host.catalog().some(tool => tool.id === entry.id && tool.enabled !== false))
|
|
231
|
+
throw new CapabilityError('The runtime no longer offers this tool.', 'UNAVAILABLE', { reason: 'pi-stale-tool' });
|
|
232
|
+
const executionGeneration = generation;
|
|
233
|
+
const input = params ?? {};
|
|
234
|
+
if (sessionFor(ctx) !== selected)
|
|
235
|
+
throw new CapabilityError('Pi workspace changed before invocation.', 'UNAVAILABLE', { reason: 'pi-stale-tool' });
|
|
236
|
+
onUpdate?.({ content: [{ type: 'text', text: 'Running in the Vibes runtime…' }], details: { toolId: entry.id } });
|
|
237
|
+
const startTime = performance.now();
|
|
238
|
+
const output = await selected.host.invokeTool(entry.id, hostToolInput(input), { ...harnessOf(ctx), signal });
|
|
239
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
240
|
+
const resultText = toolText(output);
|
|
241
|
+
if (entry.id === 'tool-search' && generation === executionGeneration && current === selected) {
|
|
242
|
+
activatePiSearchHits(pi, resultText, selected.idToName);
|
|
243
|
+
}
|
|
244
|
+
const outputText = PiStringSchema.safeParse(output);
|
|
245
|
+
const structured = outputText.success ? parseOutput(outputText.data) : PiJsonSchema.optional().parse(output);
|
|
246
|
+
return {
|
|
247
|
+
content: [{ type: 'text', text: resultText }],
|
|
248
|
+
details: {
|
|
249
|
+
toolId: entry.id,
|
|
250
|
+
exitCode: 0,
|
|
251
|
+
durationMs,
|
|
252
|
+
output: structured ?? null
|
|
253
|
+
},
|
|
254
|
+
structuredContent: structured
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if (options.enableKernelTool) {
|
|
260
|
+
ownedNames.add('vibes');
|
|
261
|
+
pi.registerTool({
|
|
262
|
+
name: 'vibes',
|
|
263
|
+
label: 'Vibes Runtime Kernel',
|
|
264
|
+
description: 'Query and invoke the Vibes runtime kernel directly. In codemode scripts, use `await tools.vibes({ op: "query", kind: "tools/tool" })` to inspect descriptors, or `await tools.vibes({ op: "invoke", id: "<tool-id>", params: {...} })` to execute tools.',
|
|
265
|
+
parameters: {
|
|
266
|
+
type: 'object',
|
|
267
|
+
properties: {
|
|
268
|
+
op: {
|
|
269
|
+
type: 'string',
|
|
270
|
+
enum: ['query', 'invoke', 'status', 'prompts'],
|
|
271
|
+
description: 'The kernel operation: query descriptors, invoke a tool, get runtime status, or list prompt manifests'
|
|
272
|
+
},
|
|
273
|
+
kind: {
|
|
274
|
+
type: 'string',
|
|
275
|
+
description: 'For query: the kind of descriptors to query, e.g. "tools/tool", "code/rules", "paths/roots"'
|
|
276
|
+
},
|
|
277
|
+
id: {
|
|
278
|
+
type: 'string',
|
|
279
|
+
description: 'For invoke: the tool id to invoke'
|
|
280
|
+
},
|
|
281
|
+
params: {
|
|
282
|
+
type: 'object',
|
|
283
|
+
description: 'For invoke: parameters to pass to the invocation'
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
required: ['op']
|
|
287
|
+
},
|
|
288
|
+
exposure: 'codemode',
|
|
289
|
+
defaultActive: false,
|
|
290
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
291
|
+
const selected = sessionFor(ctx);
|
|
292
|
+
if (selected !== registeredSession)
|
|
293
|
+
throw new CapabilityError('Pi kernel tool belongs to a replaced session.', 'UNAVAILABLE', { reason: 'pi-stale-tool' });
|
|
294
|
+
const admitted = PiKernelInputSchema.safeParse(params);
|
|
295
|
+
if (!admitted.success)
|
|
296
|
+
throw new CapabilityError('Pi kernel input is invalid.', 'INVALID_INPUT', { reason: 'pi-kernel-input-invalid', cause: admitted.error });
|
|
297
|
+
const kernel = admitted.data;
|
|
298
|
+
const op = kernel.op;
|
|
299
|
+
if (op === 'status') {
|
|
300
|
+
const status = {
|
|
301
|
+
workspace: selected.cwd,
|
|
302
|
+
root: selected.host.root,
|
|
303
|
+
phase: selected.host.runtime.phase,
|
|
304
|
+
toolCount: selected.entries.length
|
|
305
|
+
};
|
|
306
|
+
return { content: [{ type: 'text', text: JSON.stringify(status, null, 2) }], details: { op: 'status' }, structuredContent: status };
|
|
307
|
+
}
|
|
308
|
+
if (op === 'prompts') {
|
|
309
|
+
const manifests = selected.host.prompts().map((descriptor) => ({
|
|
310
|
+
id: descriptor.id,
|
|
311
|
+
name: descriptor.manifest.name,
|
|
312
|
+
type: descriptor.manifest.type,
|
|
313
|
+
description: descriptor.manifest.description ?? ''
|
|
314
|
+
}));
|
|
315
|
+
return { content: [{ type: 'text', text: JSON.stringify(manifests, null, 2) }], details: { op: 'prompts', count: manifests.length }, structuredContent: manifests };
|
|
316
|
+
}
|
|
317
|
+
if (op === 'query') {
|
|
318
|
+
const kind = kernel.kind;
|
|
319
|
+
if (!selected.host.runtime.hasKind(kind)) {
|
|
320
|
+
const registered = selected.host.runtime.kindIds();
|
|
321
|
+
const suggestions = registered.filter((id) => id.includes(kind.split('/').pop() ?? '')).slice(0, 8);
|
|
322
|
+
throw new CapabilityError(`Unknown kind "${kind}". ${suggestions.length > 0 ? `Did you mean: ${suggestions.join(', ')}?` : `Registered kinds: ${registered.slice(0, 30).join(', ')}${registered.length > 30 ? ` … (${registered.length} total)` : ''}.`}`, 'NOT_FOUND', { reason: 'pi-kind-unavailable' });
|
|
323
|
+
}
|
|
324
|
+
const descriptors = selected.host.runtime.query(kind).descriptors();
|
|
325
|
+
return { content: [{ type: 'text', text: JSON.stringify(descriptors, null, 2) }], details: { op: 'query', kind }, structuredContent: PiJsonSchema.parse(JSON.parse(JSON.stringify(descriptors))) };
|
|
326
|
+
}
|
|
327
|
+
if (op === 'invoke') {
|
|
328
|
+
const toolId = kernel.id ?? '';
|
|
329
|
+
if (!toolId)
|
|
330
|
+
throw new CapabilityError('Missing tool id.', 'INVALID_INPUT', { reason: 'pi-tool-id-required' });
|
|
331
|
+
if (!selected.idToName.has(toolId)) {
|
|
332
|
+
// Not in this session's projected catalog — suggest close
|
|
333
|
+
// matches, but do not block: the kernel can invoke any
|
|
334
|
+
// registered tool, including ones the Pi projection
|
|
335
|
+
// filtered or renamed.
|
|
336
|
+
const known = [...selected.idToName.keys()];
|
|
337
|
+
const suggestions = known.filter((id) => id.includes(toolId.split('/').pop() ?? '')).slice(0, 5);
|
|
338
|
+
if (suggestions.length > 0 && suggestions[0] !== toolId) {
|
|
339
|
+
_onUpdate?.({ content: [{ type: 'text', text: `Did you mean: ${suggestions.join(', ')}?` }], details: { suggestions } });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (sessionFor(ctx) !== selected)
|
|
343
|
+
throw new CapabilityError('Pi workspace changed before invocation.', 'UNAVAILABLE', { reason: 'pi-stale-tool' });
|
|
344
|
+
const startTime = performance.now();
|
|
345
|
+
const output = await selected.host.invokeTool(toolId, hostToolInput(kernel.params), { ...harnessOf(ctx), signal });
|
|
346
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
347
|
+
return {
|
|
348
|
+
content: [{ type: 'text', text: toolText(output) }],
|
|
349
|
+
details: { toolId, exitCode: 0, durationMs, output: output ?? null },
|
|
350
|
+
structuredContent: output
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
throw new Error(`Unsupported vibes kernel operation: "${op}". Expected "query", "invoke", or "status".`);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
// Slash commands are registered per session, from the `cli/command`
|
|
359
|
+
// descriptors that session's runtime holds. Pi has no way to unregister a
|
|
360
|
+
// command, so the set is registered once against the first session's
|
|
361
|
+
// runtime and every later session resolves the same names against its own.
|
|
362
|
+
let commandsRegistered = false;
|
|
363
|
+
const registerCommands = (host) => {
|
|
364
|
+
if (commandsRegistered)
|
|
365
|
+
return;
|
|
366
|
+
const specs = piCommandSpecs(host);
|
|
367
|
+
if (specs.length === 0)
|
|
368
|
+
return;
|
|
369
|
+
commandsRegistered = true;
|
|
370
|
+
for (const spec of specs) {
|
|
371
|
+
pi.registerCommand?.(spec.name, {
|
|
372
|
+
description: spec.description,
|
|
373
|
+
handler: async (rawArgs, ctx) => {
|
|
374
|
+
await (activation ?? (await firstActivation).activation);
|
|
375
|
+
const selected = sessionFor(ctx);
|
|
376
|
+
const live = piCommandSpecs(selected.host).find(candidate => candidate.name === spec.name);
|
|
377
|
+
if (!live)
|
|
378
|
+
throw new CapabilityError('Pi command is unavailable in this workspace.', 'UNAVAILABLE', { reason: 'pi-stale-command' });
|
|
379
|
+
await runVibesPiCommand(selected.host, live, ctx.ui, rawArgs ?? '');
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
const reportFailure = (stage, cause, host) => {
|
|
385
|
+
const failure = valueFreeFailure(CaughtFailureSchema.parse(cause));
|
|
386
|
+
if (current)
|
|
387
|
+
getLogger('integrations-pi', current.host.runtime).warn(stage, { failure });
|
|
388
|
+
host.ui?.notify?.(stage + ': ' + (failure.reason ?? failure.code ?? failure.name), 'error');
|
|
389
|
+
host.ui?.setStatus?.('vibes', 'vibes · session unavailable');
|
|
390
|
+
};
|
|
391
|
+
pi.on('session_start', async (_event, ctx) => {
|
|
392
|
+
const host = PiHostContextSchema.parse(ctx);
|
|
393
|
+
const cwd = host.cwd ?? options.workspaceRoot;
|
|
394
|
+
if (current?.cwd === cwd && activation)
|
|
395
|
+
return;
|
|
396
|
+
activation = activate(cwd, options.catalog ?? undefined).then(selected => completeActivation(selected, host));
|
|
397
|
+
announceActivation({ activation });
|
|
398
|
+
const ownedActivation = activation;
|
|
399
|
+
void settle(() => ownedActivation).then(result => {
|
|
400
|
+
if (!result.ok)
|
|
401
|
+
reportFailure('Pi activation failed', result.cause, host);
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
const onActivationLanded = async (selected, host) => {
|
|
405
|
+
if (!selected || current !== selected)
|
|
406
|
+
return;
|
|
407
|
+
applyVibesPiChrome(host.ui);
|
|
408
|
+
// Runtime events → host notifications: signal updates, check
|
|
409
|
+
// completions, git state changes surface as one-line notices
|
|
410
|
+
// without the agent polling for them.
|
|
411
|
+
stopEventForwarding = forwardRuntimeEvents(selected.host.runtime, host.ui?.notify?.bind(host.ui));
|
|
412
|
+
restrictPiToolsToDiscovery(pi, current.entries);
|
|
413
|
+
registerCommands(selected.host);
|
|
414
|
+
await bindSession(selected, host);
|
|
415
|
+
if (current !== selected || disposed)
|
|
416
|
+
return;
|
|
417
|
+
const unproject = await projectVirtualModels(pi, selected.host);
|
|
418
|
+
if (current === selected && !disposed)
|
|
419
|
+
unprojectModels = unproject;
|
|
420
|
+
else
|
|
421
|
+
unproject();
|
|
422
|
+
};
|
|
423
|
+
const completeActivation = async (selected, host) => {
|
|
424
|
+
const landed = await settle(() => onActivationLanded(selected, host));
|
|
425
|
+
if (landed.ok)
|
|
426
|
+
return selected;
|
|
427
|
+
let cause = landed.cause;
|
|
428
|
+
if (selected && current === selected) {
|
|
429
|
+
current = undefined;
|
|
430
|
+
deactivateOwned();
|
|
431
|
+
const retired = await settle(() => selected.host.dispose());
|
|
432
|
+
if (!retired.ok)
|
|
433
|
+
cause = new AggregateError([landed.cause, retired.cause], 'Pi activation and cleanup failed', { cause: landed.cause });
|
|
434
|
+
}
|
|
435
|
+
throw new CapabilityError('Pi projection activation failed.', 'UNAVAILABLE', { reason: 'pi-activation-failed', cause });
|
|
436
|
+
};
|
|
437
|
+
pi.on('session_shutdown', async () => {
|
|
438
|
+
disposed = true;
|
|
439
|
+
announceActivation({ activation: Promise.resolve(undefined) });
|
|
440
|
+
generation++;
|
|
441
|
+
const previous = current;
|
|
442
|
+
current = undefined;
|
|
443
|
+
deactivateOwned();
|
|
444
|
+
const previousPending = pendingHost;
|
|
445
|
+
pendingHost = undefined;
|
|
446
|
+
await previous?.host.dispose();
|
|
447
|
+
if (previousPending) {
|
|
448
|
+
const pending = await settle(() => previousPending);
|
|
449
|
+
if (pending.ok)
|
|
450
|
+
await pending.value.dispose();
|
|
451
|
+
else
|
|
452
|
+
reportFailure('Previous Pi boot failed', pending.cause, {});
|
|
453
|
+
}
|
|
454
|
+
if (activation) {
|
|
455
|
+
const stopped = await settle(() => activation);
|
|
456
|
+
if (!stopped.ok)
|
|
457
|
+
reportFailure('Pi activation ended during shutdown', stopped.cause, {});
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
pi.on('tool_call', async (event, ctx) => {
|
|
461
|
+
if (posture === 'off')
|
|
462
|
+
return;
|
|
463
|
+
const parsedEvent = PiToolCallEventSchema.parse(event);
|
|
464
|
+
if (!current && !disposed && ownedNames.has(parsedEvent.toolName)) {
|
|
465
|
+
await (activation ?? (await firstActivation).activation);
|
|
466
|
+
}
|
|
467
|
+
if (!current && !ownedNames.has(parsedEvent.toolName))
|
|
468
|
+
return;
|
|
469
|
+
const host = PiHostContextSchema.parse(ctx);
|
|
470
|
+
const selected = sessionFor(host);
|
|
471
|
+
const root = selected.cwd;
|
|
472
|
+
const cwd = selected.cwd;
|
|
473
|
+
const memory = options.approvalMemory ?? await getPiApprovalMemory(selected.host.runtime);
|
|
474
|
+
if (memory.runtime !== selected.host.runtime)
|
|
475
|
+
throw new CapabilityError('Pi approval memory belongs to another runtime.', 'CONFLICT', { reason: 'pi-workspace-mismatch' });
|
|
476
|
+
return await confirmMutatingTool(parsedEvent, host.ui, { memory, cwd, workspaceRoot: root, posture, host: selected.host });
|
|
477
|
+
});
|
|
478
|
+
if (injectSystemPrompt) {
|
|
479
|
+
pi.on('before_agent_start', async (input, ctx) => {
|
|
480
|
+
const event = PiExtensionEventSchema.parse(input);
|
|
481
|
+
// The first prompt can arrive while the session_start activation is
|
|
482
|
+
// still booting the runtime; the briefing names what that activation
|
|
483
|
+
// registered, so it waits for the same landing slash commands do.
|
|
484
|
+
await (activation ?? (await firstActivation).activation);
|
|
485
|
+
const selected = sessionFor(PiHostContextSchema.parse(ctx));
|
|
486
|
+
const { entries, toolByName } = selected;
|
|
487
|
+
const promptCatalog = entries.filter((entry) => new Set(PI_INITIAL_TOOL_IDS).has(entry.id));
|
|
488
|
+
const promptEntries = (promptCatalog.length > 0 ? promptCatalog : entries).map((entry) => ({
|
|
489
|
+
id: entry.id,
|
|
490
|
+
name: entry.name,
|
|
491
|
+
summary: toolByName.get(entry.name)?.description
|
|
492
|
+
}));
|
|
493
|
+
const text = event.systemPrompt ?? '';
|
|
494
|
+
if (text.includes('System prompt provenance:'))
|
|
495
|
+
return { systemPrompt: text };
|
|
496
|
+
// The session's agent identity first (the runtime's prompt
|
|
497
|
+
// manifests), then the tool briefing over what actually registered.
|
|
498
|
+
const rolePrompt = renderAgentRolePrompt(selected.host, options.promptManifestIds);
|
|
499
|
+
const briefing = buildLivePiHarnessPrompt(promptEntries, { root: selected.cwd });
|
|
500
|
+
const prompt = rolePrompt ? `${rolePrompt}\n\n${briefing}` : briefing;
|
|
501
|
+
return {
|
|
502
|
+
systemPrompt: event.systemPromptOptions?.customPrompt ? prompt + '\n\n' + text : prompt
|
|
503
|
+
};
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
// Explicit catalogs preserve the public embedded factory's eager validation;
|
|
507
|
+
// with a workspace in hand the runtime boots now too. Native discovery waits
|
|
508
|
+
// for session_start, where Pi supplies the workspace.
|
|
509
|
+
if (options.catalog != null) {
|
|
510
|
+
assertEntries(options.catalog.map((descriptor) => normalizeLiveTool(descriptor)), reserved);
|
|
511
|
+
if (options.workspaceRoot) {
|
|
512
|
+
// The eager boot is this extension's first activation: every waiter
|
|
513
|
+
// (a prompt racing the boot, an early command) settles on it.
|
|
514
|
+
activation = activate(options.workspaceRoot, options.catalog).then(selected => completeActivation(selected, {}));
|
|
515
|
+
announceActivation({ activation });
|
|
516
|
+
await activation;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
/** Joins the current activation without blocking the native session_start callback. */
|
|
521
|
+
whenReady: async () => { await (activation ?? (await firstActivation).activation); }
|
|
522
|
+
};
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
function assertEntries(tools, reserved) {
|
|
526
|
+
return assertPiToolCatalog(tools, reserved);
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* settings.json projection for a tool policy. `defaultTools` is omitted for
|
|
530
|
+
* `native`, which leaves the host default untouched; Pi keeps extension tools
|
|
531
|
+
* either way.
|
|
532
|
+
*/
|
|
533
|
+
export function piSettingsForProfile(profile = DEFAULT_PI_TOOL_POLICY_PROFILE, extensionRef = './extensions/vibes.ts') {
|
|
534
|
+
const settings = PiSettingsSchema.parse({ extensions: [extensionRef] });
|
|
535
|
+
const defaultTools = piDefaultToolsForProfile(profile, PI_BUILTIN_TOOLS);
|
|
536
|
+
if (defaultTools !== undefined)
|
|
537
|
+
settings.defaultTools = defaultTools;
|
|
538
|
+
return settings;
|
|
539
|
+
}
|
|
540
|
+
/** Default projection used by the installer: builtins off, extension loaded. */
|
|
541
|
+
export const VIBES_PI_SETTINGS = piSettingsForProfile('isolated');
|
|
542
|
+
/**
|
|
543
|
+
* Default factory Pi loads from `.pi/extensions/vibes.ts`. This module is the
|
|
544
|
+
* host entry inside the Pi process, so it hands in Pi's own environment.
|
|
545
|
+
*/
|
|
546
|
+
export { default } from "./entry.js";
|
|
547
|
+
//# sourceMappingURL=index.js.map
|