@pimote/sdk 0.15.5 → 0.16.0-rc.2
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 +41 -60
- package/dist/folders/index.d.ts +1 -1
- package/dist/folders/types.d.ts +5 -28
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/pimote/index.d.ts +23 -0
- package/dist/pimote/index.js +1 -0
- package/dist/subagents/index.d.ts +4 -0
- package/dist/subagents/index.js +1 -0
- package/dist/subagents/presentation.d.ts +228 -0
- package/dist/subagents/presentation.js +1 -0
- package/dist/subagents/provider.d.ts +30 -0
- package/dist/subagents/provider.js +124 -0
- package/dist/subagents/types.d.ts +202 -0
- package/dist/subagents/types.js +1 -0
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# @pimote/sdk
|
|
2
2
|
|
|
3
|
-
Pimote's extensibility SDK
|
|
3
|
+
Pimote's extensibility SDK provides APIs for [Pi](https://github.com/earendil-works/pi) session extensions and process-global Pimote extensions. These hosts have separate entry points:
|
|
4
4
|
|
|
5
|
-
- **`@pimote/sdk/panels`** — push structured card data to the
|
|
6
|
-
- **`@pimote/sdk/folders`** —
|
|
7
|
-
- **`@pimote/sdk/activity`** — answer
|
|
5
|
+
- **`@pimote/sdk/panels`** — push structured card data to the Pimote web client
|
|
6
|
+
- **`@pimote/sdk/folders`** — type-only folder discovery entries for Pimote extensions
|
|
7
|
+
- **`@pimote/sdk/activity`** — answer Pimote's idle-reap background-activity poll
|
|
8
|
+
- **`@pimote/sdk/subagents`** — register or discover a typed per-session subagent provider
|
|
9
|
+
- **`@pimote/sdk/pimote`** — define global `PimoteExtension` factories for sources and Settings files
|
|
8
10
|
|
|
9
|
-
The root `@pimote/sdk` import re-exports
|
|
11
|
+
The root `@pimote/sdk` import re-exports session-scoped modules and Pimote extension types.
|
|
10
12
|
|
|
11
13
|
## Install
|
|
12
14
|
|
|
@@ -112,57 +114,46 @@ Detection uses pi's EventBus for a synchronous in-process round-trip — pimote'
|
|
|
112
114
|
|
|
113
115
|
When pimote isn't present, the EventBus emit fires with no listener, `detect()` returns `null`, and there's zero overhead.
|
|
114
116
|
|
|
115
|
-
##
|
|
117
|
+
## Subagents — `@pimote/sdk/subagents`
|
|
118
|
+
|
|
119
|
+
This module defines the per-session presentation provider for interactive subagent views. A provider publishes a root-relative hierarchy, typed subagent operations, one ordered replay cursor, and leases to existing child sessions. Pimote discovers providers on the root session's EventBus; child runtimes stay owned by the extension that created them.
|
|
120
|
+
|
|
121
|
+
Use `registerProvider(pi, provider)` to publish a provider. Use `discoverProvider(pi, onProvider)` or the discovery handle to find providers, including late registration and replacement. Registration and discovery return cleanup functions or handles. Cleanup stops discovery or registration; it does not dispose the provider or child sessions. The `SubagentPresentationProvider` contract, typed operation data, cursors, snapshots, and borrowed-session lease types are exported from this module. The my-pi subagents extension implements this provider for Pimote.
|
|
122
|
+
|
|
123
|
+
Provider operations carry typed placement through `toolCallId` and `contentIndex`, or through the containing custom entry's `entryId`. Pimote's server projector suppresses the declared native renderers and places each operation as typed subagent content inside its originating assistant row, preserving surrounding native content order. Router correlation IDs stay inside my-pi. Pimote restores persisted operations through provider hydration rather than decoding extension details.
|
|
116
124
|
|
|
117
|
-
|
|
125
|
+
## Pimote extensions — `@pimote/sdk/pimote`
|
|
118
126
|
|
|
119
|
-
|
|
127
|
+
Pimote extensions are process-global modules. The host loads direct JavaScript/TypeScript modules and entry-bearing directories from `$XDG_CONFIG_HOME/pimote/extensions/`. User-scoped Pi packages can contribute entries through their `pimote.extensions` manifest field. Project packages and `pi.extensions` entries never load in this host. A module listed in both sections is an explicit dual-host module and runs twice with independent lifecycles.
|
|
120
128
|
|
|
121
129
|
```ts
|
|
122
|
-
|
|
123
|
-
import type { FolderSource, FolderCreator } from '@pimote/sdk/folders';
|
|
130
|
+
import type { PimoteExtension } from '@pimote/sdk/pimote';
|
|
124
131
|
|
|
125
|
-
|
|
126
|
-
{
|
|
132
|
+
const extension: PimoteExtension = (context) => {
|
|
133
|
+
context.folders.registerSource({
|
|
127
134
|
id: 'my-source',
|
|
128
135
|
async list() {
|
|
129
|
-
return [
|
|
130
|
-
{
|
|
131
|
-
kind: 'repo',
|
|
132
|
-
path: '/home/me/work/api',
|
|
133
|
-
name: 'api',
|
|
134
|
-
branch: 'main',
|
|
135
|
-
dirty: false,
|
|
136
|
-
ahead: 0,
|
|
137
|
-
behind: 0,
|
|
138
|
-
},
|
|
139
|
-
];
|
|
136
|
+
return [{ kind: 'repo', path: '/home/me/work/api', name: 'api', branch: 'main', dirty: false, ahead: 0, behind: 0 }];
|
|
140
137
|
},
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
},
|
|
150
|
-
async create(params) {
|
|
151
|
-
const path = `/home/me/work/${String(params.name)}`;
|
|
152
|
-
// ... scaffold the folder ...
|
|
153
|
-
return { path };
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
];
|
|
138
|
+
});
|
|
139
|
+
context.settings.registerFile('./instructions.md');
|
|
140
|
+
context.on('shutdown', async () => {
|
|
141
|
+
// Release process-global resources here.
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export default extension;
|
|
157
146
|
```
|
|
158
147
|
|
|
159
|
-
|
|
148
|
+
`registerSource()` contributes folder discovery. `registerFile()` contributes a global Settings tab. Relative file paths resolve from the extension entry module, while absolute and leading-`~` paths remain explicit. Registrations become inert after shutdown or reload. The host replaces all extension registrations on `/reload`, awaits shutdown handlers, and reports isolated diagnostics while continuing valid modules. Shutdown handlers have no timeout, so an extension must let them settle. Folder creation remains a Pimote core capability, so the public SDK has no external creator registration seam.
|
|
160
149
|
|
|
161
|
-
|
|
150
|
+
## Folders — `@pimote/sdk/folders`
|
|
162
151
|
|
|
163
|
-
|
|
152
|
+
This type-only module defines the source discovery seam used by `PimoteExtensionContext.folders.registerSource()`. Sources contribute repos and hubs. Their optional `onFolderOpen(folderPath)` hook runs before an open and can provision a missing entry. Pimote materializes standard hubs and owns the dashboard and manager folder-creation flows.
|
|
164
153
|
|
|
165
|
-
|
|
154
|
+
### `FolderSource`
|
|
155
|
+
|
|
156
|
+
`list()` returns current entries without mutation. The optional `onFolderOpen(folderPath)` hook is awaited before row, new-session, and manager opens. A thrown error aborts the open.
|
|
166
157
|
|
|
167
158
|
### Entry shapes
|
|
168
159
|
|
|
@@ -187,7 +178,7 @@ interface HubSourceEntry {
|
|
|
187
178
|
|
|
188
179
|
### `RepoInfo`
|
|
189
180
|
|
|
190
|
-
Repo facts
|
|
181
|
+
Repo facts: `path`, `name`, `branch` (`string | null`), `dirty`, `ahead`, `behind`, and optional `worktreePath`, `lastActivity` (epoch ms), `missing` (repo path no longer exists on disk), and `tags`. In a worktree hub row, `path` stays the canonical source path; `worktreePath` identifies the nested checkout whose Git status Pimote reports. It mirrors Pimote's wire `RepoInfo` type — same name, same fields — so extension authors can write `interface MyEntry extends RepoInfo` against the SDK alone.
|
|
191
182
|
|
|
192
183
|
## Activity — `@pimote/sdk/activity`
|
|
193
184
|
|
|
@@ -227,22 +218,12 @@ The pimote-side twin, used by the server's idle reaper. Performs the synchronous
|
|
|
227
218
|
|
|
228
219
|
### 0.15.0 (0.x breaking)
|
|
229
220
|
|
|
230
|
-
The
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
| `ProjectCreator` | `FolderCreator` |
|
|
237
|
-
| `ProjectCreatorDescriptor` | `FolderCreatorDescriptor` |
|
|
238
|
-
| `ProjectCreatorParamType` | `FolderCreatorParamType` |
|
|
239
|
-
| `MultiRepoSourceEntry` | `HubSourceEntry` |
|
|
240
|
-
| entry discriminator `kind: 'project'` | entry discriminator `kind: 'hub'` |
|
|
241
|
-
| `onProjectOpen(projectPath)` | `onFolderOpen(folderPath)` |
|
|
242
|
-
| config key `projectSourcesDir` | `folderSourcesDir` (legacy key still read as a deprecated fallback) |
|
|
243
|
-
| sources dir `~/.config/pimote/project-sources` | `~/.config/pimote/folder-sources` (legacy dir still loaded when the new one is absent) |
|
|
244
|
-
|
|
245
|
-
Every name in the left column is retired; each is listed here only so existing modules can be migrated. The wire command for the built-in creation flow is likewise renamed (`create_project` → `create_folder`).
|
|
221
|
+
The SDK now exposes two independent hosts:
|
|
222
|
+
|
|
223
|
+
- `@pimote/sdk/pimote` runs process-global Pimote extensions for folder sources and Settings files.
|
|
224
|
+
- `@pimote/sdk` and its `panels` and `activity` modules run inside Pi sessions through EventBus bridges.
|
|
225
|
+
|
|
226
|
+
Folder creation is a Pimote core operation. External creation registration was removed. Existing source modules must move their exported source arrays into a default `PimoteExtension` factory and call `context.folders.registerSource()`. Pi package manifests can list separate `pi.extensions` and `pimote.extensions` entries; Pimote loads only the latter from user scope.
|
|
246
227
|
|
|
247
228
|
## License
|
|
248
229
|
|
package/dist/folders/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { FolderSource,
|
|
1
|
+
export type { FolderSource, SourceEntry, RepoSourceEntry, HubSourceEntry, RepoInfo } from './types.js';
|
package/dist/folders/types.d.ts
CHANGED
|
@@ -10,6 +10,9 @@ export interface RepoInfo {
|
|
|
10
10
|
/** Commits ahead of upstream; 0 when unknown. */
|
|
11
11
|
ahead: number;
|
|
12
12
|
behind: number;
|
|
13
|
+
/** Present for worktree hub members: the nested checkout inside the hub
|
|
14
|
+
* whose git facts these are. The canonical `path` stays the source repo. */
|
|
15
|
+
worktreePath?: string;
|
|
13
16
|
/** Epoch ms of last session activity in this repo, when known. */
|
|
14
17
|
lastActivity?: number;
|
|
15
18
|
/** True when the repo path no longer exists on disk (deleted member, broken symlink). */
|
|
@@ -17,8 +20,6 @@ export interface RepoInfo {
|
|
|
17
20
|
/** Effective tags (user + source-contributed). Absent when untagged. */
|
|
18
21
|
tags?: string[];
|
|
19
22
|
}
|
|
20
|
-
/** Human-readable parameter type for a creator form field. */
|
|
21
|
-
export type FolderCreatorParamType = 'string' | 'string[]';
|
|
22
23
|
/** A repo entry contributed to the discovery index (feeds single-repo folders). */
|
|
23
24
|
export interface RepoSourceEntry extends RepoInfo {
|
|
24
25
|
kind: 'repo';
|
|
@@ -41,8 +42,8 @@ export type SourceEntry = RepoSourceEntry | HubSourceEntry;
|
|
|
41
42
|
/**
|
|
42
43
|
* Extensible discovery seam: a source contributes repos and hub
|
|
43
44
|
* folders to the folder layer. Built in: the filesystem walker over
|
|
44
|
-
* configured roots. User-authored sources are
|
|
45
|
-
*
|
|
45
|
+
* configured roots. User-authored sources are registered by the global
|
|
46
|
+
* Pimote extension host.
|
|
46
47
|
*/
|
|
47
48
|
export interface FolderSource {
|
|
48
49
|
readonly id: string;
|
|
@@ -57,27 +58,3 @@ export interface FolderSource {
|
|
|
57
58
|
*/
|
|
58
59
|
onFolderOpen?(folderPath: string): Promise<void>;
|
|
59
60
|
}
|
|
60
|
-
/** Human-readable description of a creator's form, for the creation UI. */
|
|
61
|
-
export interface FolderCreatorDescriptor {
|
|
62
|
-
label: string;
|
|
63
|
-
paramSchema: Record<string, FolderCreatorParamType>;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Extensible creation seam: a creator makes a new folder from
|
|
67
|
-
* user-supplied params. Built in: mkdir + git init. User-authored creators
|
|
68
|
-
* are dynamic-imported from the configured folder-sources directory.
|
|
69
|
-
*/
|
|
70
|
-
export interface FolderCreator {
|
|
71
|
-
readonly id: string;
|
|
72
|
-
/** Human-readable description for the creation UI. */
|
|
73
|
-
describe(): FolderCreatorDescriptor;
|
|
74
|
-
/**
|
|
75
|
-
* Make the folder and return its path. Pimote resolves the path to its
|
|
76
|
-
* canonical filesystem identity before seeding, broadcasting, or opening a
|
|
77
|
-
* session, so a creator can return the lexical path it used to reach the
|
|
78
|
-
* folder. The path must name the successfully created folder.
|
|
79
|
-
*/
|
|
80
|
-
create(params: Record<string, unknown>): Promise<{
|
|
81
|
-
path: string;
|
|
82
|
-
}>;
|
|
83
|
-
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A server-global Pimote extension.
|
|
3
|
+
*
|
|
4
|
+
* The host creates a fresh context for each runtime. Extensions must keep all
|
|
5
|
+
* registrations behind that context because the host invalidates it on reload.
|
|
6
|
+
*/
|
|
7
|
+
export type PimoteExtension = (context: PimoteExtensionContext) => void | Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Context exposed to server-global Pimote extensions.
|
|
10
|
+
*
|
|
11
|
+
* This is intentionally separate from pi's session-scoped ExtensionAPI. Pimote
|
|
12
|
+
* extensions contribute folder discovery and global settings files only.
|
|
13
|
+
*/
|
|
14
|
+
export interface PimoteExtensionContext {
|
|
15
|
+
readonly folders: {
|
|
16
|
+
registerSource(source: import('../folders/types.js').FolderSource): void;
|
|
17
|
+
};
|
|
18
|
+
readonly settings: {
|
|
19
|
+
registerFile(path: string): void;
|
|
20
|
+
};
|
|
21
|
+
on(event: 'shutdown', handler: () => void | Promise<void>): () => void;
|
|
22
|
+
}
|
|
23
|
+
export type PimoteShutdownHandler = () => void | Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { AgentPath, BorrowedAgentSession, BorrowedSessionManager, FeedCursor, PiEventRef, SubagentAwaitChildStatus, SubagentCapabilities, SubagentLease, SubagentLeaseEvent, SubagentNode, SubagentNodeEventData, SubagentNodeSnapshot, SubagentNodeState, SubagentOperation, SubagentOwnerControlPort, SubagentProviderDiscovery, SubagentProviderRequest, SubagentProviderResponse, SubagentResurrectRequest, SubagentResurrectResult, SubagentState, SubagentStopRequest, SubagentTeardownReason, SubagentTeardownResult, SubagentUsage, SubagentUsageSnapshot, SubagentRuntimeReplacedData, SubagentCostBreakdown, } from './types.js';
|
|
2
|
+
export { SUBAGENT_PROVIDER_REQUEST, SUBAGENT_PROVIDER_RESPONSE, discover, discoverProvider, register, registerProvider } from './provider.js';
|
|
3
|
+
export type { DiscoverProviderOptions } from './provider.js';
|
|
4
|
+
export type { ActiveSubagentOperation, PersistedSubagentContent, SubagentDelegatedChild, SubagentInterruptResult, SubagentInterruptTarget, SubagentResurrectedAgent, SubagentAwaitResult, SubagentAwaitStatus, SubagentCompletionResult, SubagentDeltaData, SubagentDeltaEvent, SubagentEndData, SubagentEndEvent, SubagentFiniteData, SubagentFiniteEvent, SubagentFinalData, SubagentOperationKind, SubagentOperationTarget, SubagentPlacement, SubagentPresentationManifest, SubagentPresentationProvider, SubagentPresentationReplay, SubagentProtocolErrorData, SubagentProviderEvent, SubagentProviderSnapshot, SubagentStartData, SubagentStartEvent, SubagentTopologyEvent, } from './presentation.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SUBAGENT_PROVIDER_REQUEST, SUBAGENT_PROVIDER_RESPONSE, discover, discoverProvider, register, registerProvider } from './provider.js';
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import type { AgentPath, BorrowedAgentSession, FeedCursor, PiEventRef, SubagentNodeEventData, SubagentNodeSnapshot, SubagentProviderControls, SubagentRuntimeReplacedData, SubagentUsage } from './types.js';
|
|
2
|
+
export type SubagentPresentationManifest = Readonly<{
|
|
3
|
+
hiddenToolNames: readonly string[];
|
|
4
|
+
hiddenCustomTypes: readonly string[];
|
|
5
|
+
}>;
|
|
6
|
+
export type SubagentOperationKind = 'send' | 'respond' | 'await' | 'delegation' | 'completion' | 'interrupt' | 'teardown' | 'resurrect';
|
|
7
|
+
export type SubagentPlacement = Readonly<{
|
|
8
|
+
toolCallId: string;
|
|
9
|
+
contentIndex: number;
|
|
10
|
+
entryId?: string;
|
|
11
|
+
}> | Readonly<{
|
|
12
|
+
entryId: string;
|
|
13
|
+
}>;
|
|
14
|
+
export type SubagentOperationTarget = Readonly<{
|
|
15
|
+
operationId: string;
|
|
16
|
+
kind: SubagentOperationKind;
|
|
17
|
+
path: AgentPath;
|
|
18
|
+
placement?: SubagentPlacement;
|
|
19
|
+
}>;
|
|
20
|
+
export type SubagentAwaitStatus = Readonly<{
|
|
21
|
+
path: AgentPath;
|
|
22
|
+
state: 'running' | 'waiting' | 'idle' | 'errored' | 'dead' | 'stopped';
|
|
23
|
+
output?: string;
|
|
24
|
+
error?: string;
|
|
25
|
+
sessionId?: string;
|
|
26
|
+
}>;
|
|
27
|
+
export type SubagentCompletionResult = Readonly<{
|
|
28
|
+
state: 'idle' | 'errored' | 'dead' | 'stopped';
|
|
29
|
+
output?: string;
|
|
30
|
+
error?: string;
|
|
31
|
+
usage?: SubagentUsage;
|
|
32
|
+
}>;
|
|
33
|
+
export type SubagentAwaitResult = Readonly<{
|
|
34
|
+
statuses: readonly SubagentAwaitStatus[];
|
|
35
|
+
interrupted?: boolean;
|
|
36
|
+
error?: string;
|
|
37
|
+
}>;
|
|
38
|
+
export type SubagentStartData = Readonly<{
|
|
39
|
+
operation: 'send';
|
|
40
|
+
to: AgentPath;
|
|
41
|
+
request: string;
|
|
42
|
+
response: string;
|
|
43
|
+
phase: 'ready' | 'sending' | 'waiting' | 'responding';
|
|
44
|
+
}> | Readonly<{
|
|
45
|
+
operation: 'respond';
|
|
46
|
+
body: string;
|
|
47
|
+
}> | Readonly<{
|
|
48
|
+
operation: 'await';
|
|
49
|
+
requested: readonly AgentPath[];
|
|
50
|
+
statuses: readonly SubagentAwaitStatus[];
|
|
51
|
+
}>;
|
|
52
|
+
export type SubagentDeltaData = Readonly<{
|
|
53
|
+
operation: 'send';
|
|
54
|
+
field: 'request' | 'response';
|
|
55
|
+
delta: string;
|
|
56
|
+
}> | Readonly<{
|
|
57
|
+
operation: 'send';
|
|
58
|
+
phase: 'waiting' | 'responding';
|
|
59
|
+
}> | Readonly<{
|
|
60
|
+
operation: 'respond';
|
|
61
|
+
delta: string;
|
|
62
|
+
}> | Readonly<{
|
|
63
|
+
operation: 'await';
|
|
64
|
+
statuses: readonly SubagentAwaitStatus[];
|
|
65
|
+
}>;
|
|
66
|
+
export type SubagentEndData = Readonly<{
|
|
67
|
+
operation: 'send';
|
|
68
|
+
to: AgentPath;
|
|
69
|
+
request: string;
|
|
70
|
+
response: string;
|
|
71
|
+
error?: string;
|
|
72
|
+
}> | Readonly<{
|
|
73
|
+
operation: 'respond';
|
|
74
|
+
body: string;
|
|
75
|
+
error?: string;
|
|
76
|
+
}> | Readonly<{
|
|
77
|
+
operation: 'await';
|
|
78
|
+
result: SubagentAwaitResult;
|
|
79
|
+
}>;
|
|
80
|
+
export type SubagentDelegatedChild = Readonly<{
|
|
81
|
+
path: AgentPath;
|
|
82
|
+
task: string;
|
|
83
|
+
persona?: string;
|
|
84
|
+
}>;
|
|
85
|
+
export type SubagentInterruptTarget = Readonly<{
|
|
86
|
+
path: AgentPath;
|
|
87
|
+
output?: string;
|
|
88
|
+
error?: string;
|
|
89
|
+
}>;
|
|
90
|
+
export type SubagentInterruptResult = Readonly<{
|
|
91
|
+
interrupted: readonly SubagentInterruptTarget[];
|
|
92
|
+
pending: readonly SubagentInterruptTarget[];
|
|
93
|
+
failed: readonly SubagentInterruptTarget[];
|
|
94
|
+
}>;
|
|
95
|
+
export type SubagentResurrectedAgent = Readonly<{
|
|
96
|
+
path: AgentPath;
|
|
97
|
+
sessionId: string;
|
|
98
|
+
incarnation: string;
|
|
99
|
+
state: 'idle' | 'running';
|
|
100
|
+
usage: SubagentUsage;
|
|
101
|
+
output?: string;
|
|
102
|
+
error?: string;
|
|
103
|
+
}>;
|
|
104
|
+
export type SubagentFiniteData = Readonly<{
|
|
105
|
+
operation: 'delegation';
|
|
106
|
+
children: readonly SubagentDelegatedChild[];
|
|
107
|
+
}> | Readonly<{
|
|
108
|
+
operation: 'completion';
|
|
109
|
+
result: SubagentCompletionResult;
|
|
110
|
+
}> | Readonly<{
|
|
111
|
+
operation: 'interrupt';
|
|
112
|
+
result: SubagentInterruptResult;
|
|
113
|
+
}> | Readonly<{
|
|
114
|
+
operation: 'teardown';
|
|
115
|
+
result: Readonly<{
|
|
116
|
+
affectedAgents: readonly Readonly<{
|
|
117
|
+
path: AgentPath;
|
|
118
|
+
sessionId: string;
|
|
119
|
+
incarnation: string;
|
|
120
|
+
state: SubagentAwaitStatus['state'];
|
|
121
|
+
usage: SubagentUsage;
|
|
122
|
+
output?: string;
|
|
123
|
+
error?: string;
|
|
124
|
+
}>[];
|
|
125
|
+
status: 'removed' | 'already_removed' | 'not_found';
|
|
126
|
+
}>;
|
|
127
|
+
}> | Readonly<{
|
|
128
|
+
operation: 'resurrect';
|
|
129
|
+
result: Readonly<{
|
|
130
|
+
affectedAgents: readonly SubagentResurrectedAgent[];
|
|
131
|
+
}>;
|
|
132
|
+
}> | Readonly<{
|
|
133
|
+
operation: 'protocol_error';
|
|
134
|
+
message: string;
|
|
135
|
+
}>;
|
|
136
|
+
export type SubagentFinalData = SubagentEndData | SubagentFiniteData;
|
|
137
|
+
export type SubagentProtocolErrorData = Readonly<{
|
|
138
|
+
operationId: string;
|
|
139
|
+
operation: SubagentOperationKind;
|
|
140
|
+
message: string;
|
|
141
|
+
}>;
|
|
142
|
+
export type ActiveSubagentOperation = Readonly<{
|
|
143
|
+
target: SubagentOperationTarget;
|
|
144
|
+
data: SubagentStartData;
|
|
145
|
+
}>;
|
|
146
|
+
type PresentationEventBase = Readonly<{
|
|
147
|
+
eventId: string;
|
|
148
|
+
cursor: FeedCursor;
|
|
149
|
+
target: SubagentOperationTarget;
|
|
150
|
+
}>;
|
|
151
|
+
export type SubagentStartEvent = PresentationEventBase & Readonly<{
|
|
152
|
+
type: 'subagent_start';
|
|
153
|
+
data: SubagentStartData;
|
|
154
|
+
}>;
|
|
155
|
+
export type SubagentDeltaEvent = PresentationEventBase & Readonly<{
|
|
156
|
+
type: 'subagent_delta';
|
|
157
|
+
data: SubagentDeltaData;
|
|
158
|
+
}>;
|
|
159
|
+
export type SubagentEndEvent = PresentationEventBase & Readonly<{
|
|
160
|
+
type: 'subagent_end';
|
|
161
|
+
state: 'complete' | 'error' | 'aborted';
|
|
162
|
+
data: SubagentEndData;
|
|
163
|
+
}>;
|
|
164
|
+
export type SubagentFiniteEvent = PresentationEventBase & Readonly<{
|
|
165
|
+
type: 'subagent_snapshot_event';
|
|
166
|
+
state: 'complete' | 'error' | 'aborted';
|
|
167
|
+
data: SubagentFinalData;
|
|
168
|
+
}>;
|
|
169
|
+
type SubagentTopologyData = Readonly<{
|
|
170
|
+
kind: 'node_added' | 'node_updated';
|
|
171
|
+
data: SubagentNodeEventData;
|
|
172
|
+
}> | Readonly<{
|
|
173
|
+
kind: 'node_removed';
|
|
174
|
+
data: Readonly<{
|
|
175
|
+
node: SubagentNodeSnapshot;
|
|
176
|
+
reason: string;
|
|
177
|
+
}>;
|
|
178
|
+
}> | Readonly<{
|
|
179
|
+
kind: 'runtime_replaced';
|
|
180
|
+
data: SubagentRuntimeReplacedData;
|
|
181
|
+
}>;
|
|
182
|
+
export type SubagentTopologyEvent = Readonly<{
|
|
183
|
+
eventId: string;
|
|
184
|
+
cursor: FeedCursor;
|
|
185
|
+
path: AgentPath;
|
|
186
|
+
relatedPiEvents: readonly PiEventRef[];
|
|
187
|
+
}> & SubagentTopologyData;
|
|
188
|
+
export type SubagentProviderEvent = SubagentTopologyEvent | SubagentStartEvent | SubagentDeltaEvent | SubagentEndEvent | SubagentFiniteEvent;
|
|
189
|
+
export type PersistedSubagentContent = Readonly<{
|
|
190
|
+
operationId: string;
|
|
191
|
+
kind: SubagentOperationKind;
|
|
192
|
+
state: 'complete' | 'error' | 'aborted';
|
|
193
|
+
data: SubagentFinalData;
|
|
194
|
+
}> & (Readonly<{
|
|
195
|
+
source: 'tool';
|
|
196
|
+
placement: Extract<SubagentPlacement, {
|
|
197
|
+
toolCallId: string;
|
|
198
|
+
}>;
|
|
199
|
+
}> | Readonly<{
|
|
200
|
+
source: 'custom_entry';
|
|
201
|
+
placement?: Extract<SubagentPlacement, {
|
|
202
|
+
entryId: string;
|
|
203
|
+
}>;
|
|
204
|
+
}>);
|
|
205
|
+
export type SubagentProviderSnapshot = Readonly<{
|
|
206
|
+
manifest: SubagentPresentationManifest;
|
|
207
|
+
nodes: readonly SubagentNodeSnapshot[];
|
|
208
|
+
cursor: FeedCursor;
|
|
209
|
+
activeOperations: readonly ActiveSubagentOperation[];
|
|
210
|
+
}>;
|
|
211
|
+
export type SubagentPresentationReplay = Readonly<{
|
|
212
|
+
kind: 'events';
|
|
213
|
+
cursor: FeedCursor;
|
|
214
|
+
events: readonly SubagentProviderEvent[];
|
|
215
|
+
}> | Readonly<{
|
|
216
|
+
kind: 'snapshot_required';
|
|
217
|
+
reason: 'epoch_changed' | 'history_unavailable';
|
|
218
|
+
}>;
|
|
219
|
+
export interface SubagentPresentationProvider extends SubagentProviderControls {
|
|
220
|
+
snapshot(): SubagentProviderSnapshot;
|
|
221
|
+
subscribe(listener: (event: SubagentProviderEvent) => void): () => void;
|
|
222
|
+
replay(after?: FeedCursor): SubagentPresentationReplay;
|
|
223
|
+
rehydrate(input: {
|
|
224
|
+
session: BorrowedAgentSession;
|
|
225
|
+
path: AgentPath;
|
|
226
|
+
}): Promise<void>;
|
|
227
|
+
}
|
|
228
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import type { SubagentEventBusOwner, SubagentProviderDiscovery } from './types.js';
|
|
3
|
+
import type { SubagentPresentationProvider } from './presentation.js';
|
|
4
|
+
type RegisteredProvider = SubagentPresentationProvider;
|
|
5
|
+
/** EventBus channel emitted by a Pimote host to discover a provider. */
|
|
6
|
+
export declare const SUBAGENT_PROVIDER_REQUEST: "pimote:subagents:request";
|
|
7
|
+
/** EventBus channel carrying the currently registered provider. */
|
|
8
|
+
export declare const SUBAGENT_PROVIDER_RESPONSE: "pimote:subagents:response";
|
|
9
|
+
export declare function registerProvider(owner: SubagentEventBusOwner | ExtensionAPI, provider: RegisteredProvider): () => void;
|
|
10
|
+
export interface DiscoverProviderOptions {
|
|
11
|
+
/** Called whenever the provider first appears or is replaced. */
|
|
12
|
+
onProvider?: (provider: RegisteredProvider) => void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Discover the provider on a session EventBus and keep listening for late
|
|
16
|
+
* registration or replacement.
|
|
17
|
+
*
|
|
18
|
+
* With a callback, the return value is an unsubscribe function. Without a
|
|
19
|
+
* callback, the return value is a discovery handle whose `provider` getter
|
|
20
|
+
* reflects the latest registration. In both forms discovery emits one request
|
|
21
|
+
* after installing its response listener, so a provider registered before or
|
|
22
|
+
* during the handshake cannot be missed.
|
|
23
|
+
*/
|
|
24
|
+
export declare function discoverProvider(owner: SubagentEventBusOwner | ExtensionAPI, onProvider: (provider: RegisteredProvider) => void): () => void;
|
|
25
|
+
export declare function discoverProvider(owner: SubagentEventBusOwner | ExtensionAPI, options?: DiscoverProviderOptions): SubagentProviderDiscovery;
|
|
26
|
+
/** Short alias for hosts that use the request/response verb. */
|
|
27
|
+
export declare const discover: typeof discoverProvider;
|
|
28
|
+
/** Short alias for extensions that use the registration verb. */
|
|
29
|
+
export declare const register: typeof registerProvider;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/** EventBus channel emitted by a Pimote host to discover a provider. */
|
|
2
|
+
export const SUBAGENT_PROVIDER_REQUEST = 'pimote:subagents:request';
|
|
3
|
+
/** EventBus channel carrying the currently registered provider. */
|
|
4
|
+
export const SUBAGENT_PROVIDER_RESPONSE = 'pimote:subagents:response';
|
|
5
|
+
/**
|
|
6
|
+
* Narrow an ExtensionAPI/EventBus-like value to its EventBus.
|
|
7
|
+
*
|
|
8
|
+
* The SDK keeps this adapter structural so extensions can use the same seam in
|
|
9
|
+
* tests without constructing a complete ExtensionAPI object.
|
|
10
|
+
*/
|
|
11
|
+
function eventBusOf(owner) {
|
|
12
|
+
if ('events' in owner)
|
|
13
|
+
return owner.events;
|
|
14
|
+
return owner;
|
|
15
|
+
}
|
|
16
|
+
function providerFrom(data) {
|
|
17
|
+
if (data && typeof data === 'object' && 'provider' in data) {
|
|
18
|
+
const provider = data.provider;
|
|
19
|
+
return provider && typeof provider === 'object' ? provider : undefined;
|
|
20
|
+
}
|
|
21
|
+
return data && typeof data === 'object' ? data : undefined;
|
|
22
|
+
}
|
|
23
|
+
function response(provider) {
|
|
24
|
+
return { provider };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* One provider registration per session EventBus.
|
|
28
|
+
*
|
|
29
|
+
* Registration is a live responder rather than a one-shot value. A host can
|
|
30
|
+
* begin discovery before the extension binds, and a later registration or
|
|
31
|
+
* replacement is delivered immediately. Calling this again on the same bus
|
|
32
|
+
* deactivates the previous responder. The function never owns or disposes the
|
|
33
|
+
* provider; the owner controls its lifecycle.
|
|
34
|
+
*/
|
|
35
|
+
const registrations = new WeakMap();
|
|
36
|
+
export function registerProvider(owner, provider) {
|
|
37
|
+
const bus = eventBusOf(owner);
|
|
38
|
+
const key = bus;
|
|
39
|
+
const previous = registrations.get(key);
|
|
40
|
+
previous?.unsubscribeRequest();
|
|
41
|
+
if (previous)
|
|
42
|
+
previous.active = false;
|
|
43
|
+
const registration = {
|
|
44
|
+
active: true,
|
|
45
|
+
unsubscribeRequest: () => undefined,
|
|
46
|
+
};
|
|
47
|
+
registration.unsubscribeRequest = bus.on(SUBAGENT_PROVIDER_REQUEST, () => {
|
|
48
|
+
if (!registration.active)
|
|
49
|
+
return;
|
|
50
|
+
bus.emit(SUBAGENT_PROVIDER_RESPONSE, response(provider));
|
|
51
|
+
});
|
|
52
|
+
registrations.set(key, registration);
|
|
53
|
+
// Hosts that already started discovery receive a replacement without a
|
|
54
|
+
// second request. Hosts that have not started simply have this value ignored.
|
|
55
|
+
bus.emit(SUBAGENT_PROVIDER_RESPONSE, response(provider));
|
|
56
|
+
return () => {
|
|
57
|
+
if (!registration.active)
|
|
58
|
+
return;
|
|
59
|
+
registration.active = false;
|
|
60
|
+
registration.unsubscribeRequest();
|
|
61
|
+
if (registrations.get(key) === registration)
|
|
62
|
+
registrations.delete(key);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function discoverProvider(owner, callbackOrOptions) {
|
|
66
|
+
const bus = eventBusOf(owner);
|
|
67
|
+
let current;
|
|
68
|
+
let closed = false;
|
|
69
|
+
const listeners = new Set();
|
|
70
|
+
const callback = typeof callbackOrOptions === 'function' ? callbackOrOptions : callbackOrOptions?.onProvider;
|
|
71
|
+
if (callback)
|
|
72
|
+
listeners.add((provider) => {
|
|
73
|
+
if (provider)
|
|
74
|
+
callback(provider);
|
|
75
|
+
});
|
|
76
|
+
const publish = (provider) => {
|
|
77
|
+
if (closed || provider === current)
|
|
78
|
+
return;
|
|
79
|
+
current = provider;
|
|
80
|
+
for (const listener of [...listeners])
|
|
81
|
+
listener(provider);
|
|
82
|
+
};
|
|
83
|
+
const unsubscribeResponse = bus.on(SUBAGENT_PROVIDER_RESPONSE, (data) => publish(providerFrom(data)));
|
|
84
|
+
bus.emit(SUBAGENT_PROVIDER_REQUEST, {});
|
|
85
|
+
const close = () => {
|
|
86
|
+
if (closed)
|
|
87
|
+
return;
|
|
88
|
+
closed = true;
|
|
89
|
+
unsubscribeResponse();
|
|
90
|
+
listeners.clear();
|
|
91
|
+
};
|
|
92
|
+
if (callback)
|
|
93
|
+
return close;
|
|
94
|
+
const discovery = {
|
|
95
|
+
get provider() {
|
|
96
|
+
return current;
|
|
97
|
+
},
|
|
98
|
+
get current() {
|
|
99
|
+
return current;
|
|
100
|
+
},
|
|
101
|
+
subscribe(listener) {
|
|
102
|
+
if (closed)
|
|
103
|
+
return () => undefined;
|
|
104
|
+
listeners.add(listener);
|
|
105
|
+
if (current)
|
|
106
|
+
listener(current);
|
|
107
|
+
return () => listeners.delete(listener);
|
|
108
|
+
},
|
|
109
|
+
onProvider(listener) {
|
|
110
|
+
if (closed)
|
|
111
|
+
return () => undefined;
|
|
112
|
+
listeners.add(listener);
|
|
113
|
+
if (current)
|
|
114
|
+
listener(current);
|
|
115
|
+
return () => listeners.delete(listener);
|
|
116
|
+
},
|
|
117
|
+
close,
|
|
118
|
+
};
|
|
119
|
+
return discovery;
|
|
120
|
+
}
|
|
121
|
+
/** Short alias for hosts that use the request/response verb. */
|
|
122
|
+
export const discover = discoverProvider;
|
|
123
|
+
/** Short alias for extensions that use the registration verb. */
|
|
124
|
+
export const register = registerProvider;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import type { AgentSession, EventBus } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
/** Root-relative identity of a my-pi child runtime. */
|
|
3
|
+
export type AgentPath = readonly string[];
|
|
4
|
+
/** Provider-owned replay position. Pimote keeps this separate from parent cursors. */
|
|
5
|
+
export type FeedCursor = Readonly<{
|
|
6
|
+
epoch: string;
|
|
7
|
+
sequence: number;
|
|
8
|
+
}>;
|
|
9
|
+
/** Native Pi transcript/session provenance for one semantic event. */
|
|
10
|
+
export type PiEventRef = Readonly<{
|
|
11
|
+
sessionId: string;
|
|
12
|
+
entryId?: string;
|
|
13
|
+
toolCallId?: string;
|
|
14
|
+
kind: 'message' | 'tool_call' | 'custom_message' | 'lifecycle';
|
|
15
|
+
}>;
|
|
16
|
+
/** One child state included in await progress and settlement facts. */
|
|
17
|
+
export type SubagentAwaitChildStatus = Readonly<{
|
|
18
|
+
path: AgentPath;
|
|
19
|
+
state: 'running' | 'waiting' | 'idle' | 'errored' | 'dead' | 'stopped';
|
|
20
|
+
output?: string;
|
|
21
|
+
error?: string;
|
|
22
|
+
sessionId?: string;
|
|
23
|
+
}>;
|
|
24
|
+
/** A public node projection carried by topology events and snapshots. */
|
|
25
|
+
export type SubagentNodeSnapshot = Readonly<{
|
|
26
|
+
path: AgentPath;
|
|
27
|
+
/** Stable for runtime replacement. Changes when a path is removed or reused. */
|
|
28
|
+
incarnation: string;
|
|
29
|
+
sessionId: string;
|
|
30
|
+
task?: string;
|
|
31
|
+
persona?: string;
|
|
32
|
+
personaIcon?: string;
|
|
33
|
+
model?: string;
|
|
34
|
+
thinkingLevel?: string;
|
|
35
|
+
state: SubagentState;
|
|
36
|
+
activity?: string;
|
|
37
|
+
usage: SubagentUsage;
|
|
38
|
+
/** Context fill as an integer percentage from 0 through 100. */
|
|
39
|
+
contextFill?: number;
|
|
40
|
+
channels: readonly string[];
|
|
41
|
+
waitingFor: readonly AgentPath[];
|
|
42
|
+
hasSubgroup: boolean;
|
|
43
|
+
lastOutput?: string;
|
|
44
|
+
lastError?: string;
|
|
45
|
+
}>;
|
|
46
|
+
/** Compatibility alias for consumers that call hierarchy rows nodes. */
|
|
47
|
+
export type SubagentNode = SubagentNodeSnapshot;
|
|
48
|
+
export type SubagentUsageSnapshot = SubagentUsage;
|
|
49
|
+
/** Topology events accept the complete projection and old session-id facts. */
|
|
50
|
+
export type SubagentNodeEventData = SubagentNodeSnapshot | Readonly<{
|
|
51
|
+
sessionId: string;
|
|
52
|
+
}>;
|
|
53
|
+
export type SubagentRuntimeReplacedData = Readonly<{
|
|
54
|
+
oldSessionId: string;
|
|
55
|
+
newSessionId: string;
|
|
56
|
+
}>;
|
|
57
|
+
export type SubagentState = 'running' | 'waiting' | 'idle' | 'errored' | 'dead';
|
|
58
|
+
export type SubagentNodeState = SubagentState;
|
|
59
|
+
export type SubagentCostBreakdown = Readonly<{
|
|
60
|
+
input: number;
|
|
61
|
+
output: number;
|
|
62
|
+
cacheRead: number;
|
|
63
|
+
cacheWrite: number;
|
|
64
|
+
}>;
|
|
65
|
+
/** Provider usage accumulated for one child runtime. */
|
|
66
|
+
export type SubagentUsage = Readonly<{
|
|
67
|
+
input: number;
|
|
68
|
+
output: number;
|
|
69
|
+
cacheRead: number;
|
|
70
|
+
cacheWrite: number;
|
|
71
|
+
cost: number;
|
|
72
|
+
costBreakdown?: SubagentCostBreakdown;
|
|
73
|
+
turns: number;
|
|
74
|
+
}>;
|
|
75
|
+
export type SubagentTeardownReason = 'not_found' | 'already_removed' | 'removed';
|
|
76
|
+
export type SubagentTeardownResult = Readonly<{
|
|
77
|
+
path: AgentPath;
|
|
78
|
+
removed: boolean;
|
|
79
|
+
sessionId?: string;
|
|
80
|
+
incarnation?: string;
|
|
81
|
+
reason?: SubagentTeardownReason;
|
|
82
|
+
}>;
|
|
83
|
+
/** Guarded request to stop the child that owns one public path incarnation. */
|
|
84
|
+
export type SubagentStopRequest = Readonly<{
|
|
85
|
+
path: AgentPath;
|
|
86
|
+
incarnation: string;
|
|
87
|
+
/** The host rejects a request for a replacement runtime at this path. */
|
|
88
|
+
expectedSessionId: string;
|
|
89
|
+
}>;
|
|
90
|
+
/** Input to the normal my-pi resurrection path. */
|
|
91
|
+
export type SubagentResurrectRequest = Readonly<{
|
|
92
|
+
/** Saved child session id returned by teardown. */
|
|
93
|
+
sessionId: string;
|
|
94
|
+
/** Owning root-relative parent path. */
|
|
95
|
+
parentPath: AgentPath;
|
|
96
|
+
/** New local id under `parentPath`. */
|
|
97
|
+
localId: string;
|
|
98
|
+
task: string;
|
|
99
|
+
channels: readonly string[];
|
|
100
|
+
model?: string;
|
|
101
|
+
}>;
|
|
102
|
+
/** Result of a normal resurrection operation. */
|
|
103
|
+
export type SubagentResurrectResult = Readonly<{
|
|
104
|
+
path: AgentPath;
|
|
105
|
+
node?: SubagentNodeSnapshot;
|
|
106
|
+
sessionId?: string;
|
|
107
|
+
incarnation?: string;
|
|
108
|
+
}>;
|
|
109
|
+
/** Operations that preserve the identity of the borrowed child runtime. */
|
|
110
|
+
export type SubagentOperation = 'prompt' | 'steer' | 'follow_up' | 'stop' | 'bash' | 'abort_bash' | 'set_model' | 'cycle_model' | 'set_thinking_level' | 'cycle_thinking_level' | 'set_auto_compaction' | 'compact' | 'tree' | 'completion' | 'state' | 'meta';
|
|
111
|
+
/** Provider-owned child capabilities. It never exposes extension machinery. */
|
|
112
|
+
export interface SubagentCapabilities {
|
|
113
|
+
/** Explicitly admitted identity-preserving operations. */
|
|
114
|
+
readonly operations?: readonly SubagentOperation[];
|
|
115
|
+
/** Whether the child renderer can expose a view. */
|
|
116
|
+
readonly renderer?: Readonly<{
|
|
117
|
+
visible?: boolean;
|
|
118
|
+
[key: string]: unknown;
|
|
119
|
+
}>;
|
|
120
|
+
/** Child-local model resolution and settings. */
|
|
121
|
+
readonly resolveModel?: (provider: string, modelId: string) => Promise<unknown> | unknown;
|
|
122
|
+
readonly getSettings?: () => unknown;
|
|
123
|
+
/** Native bash admission and dispatch. */
|
|
124
|
+
readonly nativeBash?: Readonly<{
|
|
125
|
+
admitted?: boolean;
|
|
126
|
+
execute?: (...args: readonly unknown[]) => Promise<unknown> | unknown;
|
|
127
|
+
abort?: (...args: readonly unknown[]) => Promise<void> | void;
|
|
128
|
+
[key: string]: unknown;
|
|
129
|
+
}>;
|
|
130
|
+
readonly executeBash?: (...args: readonly unknown[]) => Promise<unknown> | unknown;
|
|
131
|
+
/** Filtered command and argument-completion descriptors. */
|
|
132
|
+
readonly getCommands?: (...args: readonly unknown[]) => Promise<unknown> | unknown;
|
|
133
|
+
readonly completeArgs?: (...args: readonly unknown[]) => Promise<unknown> | unknown;
|
|
134
|
+
readonly completeFileRefs?: (...args: readonly unknown[]) => Promise<unknown> | unknown;
|
|
135
|
+
readonly [key: string]: unknown;
|
|
136
|
+
}
|
|
137
|
+
/** Minimal session-manager projection exposed beside a borrowed native session. */
|
|
138
|
+
export type BorrowedSessionManager = Pick<AgentSession['sessionManager'], 'getEntries' | 'getBranch' | 'buildContextEntries' | 'getTree' | 'getLeafId' | 'getCwd' | 'getLabel' | 'appendLabelChange'>;
|
|
139
|
+
/**
|
|
140
|
+
* Structural projection of the native child runtime borrowed by a view.
|
|
141
|
+
*
|
|
142
|
+
* The projection intentionally omits dispose, runtime replacement, session creation,
|
|
143
|
+
* fork/switch/reload, extension binding, and unrestricted manager mutation.
|
|
144
|
+
*/
|
|
145
|
+
export type BorrowedAgentSession = Pick<AgentSession, 'sessionId' | 'sessionFile' | 'messages' | 'model' | 'thinkingLevel' | 'isStreaming' | 'isIdle' | 'isCompacting' | 'subscribe' | 'prompt' | 'steer' | 'followUp' | 'abort' | 'executeBash' | 'abortBash' | 'setModel' | 'cycleModel' | 'setThinkingLevel' | 'cycleThinkingLevel' | 'setAutoCompactionEnabled' | 'compact' | 'navigateTree' | 'getContextUsage' | 'getSessionStats'> & {
|
|
146
|
+
readonly sessionManager: BorrowedSessionManager;
|
|
147
|
+
/** Full-resync native state projection. */
|
|
148
|
+
readonly state: Pick<AgentSession['state'], 'streamingMessage' | 'pendingToolCalls' | 'errorMessage'>;
|
|
149
|
+
};
|
|
150
|
+
export type SubagentLeaseEvent = Readonly<{
|
|
151
|
+
kind: 'rebound';
|
|
152
|
+
session: BorrowedAgentSession;
|
|
153
|
+
capabilities?: SubagentCapabilities;
|
|
154
|
+
}> | Readonly<{
|
|
155
|
+
kind: 'closed';
|
|
156
|
+
reason: 'removed' | 'unavailable';
|
|
157
|
+
}>;
|
|
158
|
+
/** A borrowed child runtime. Release is idempotent and never disposes it. */
|
|
159
|
+
export interface SubagentLease {
|
|
160
|
+
readonly path: AgentPath;
|
|
161
|
+
/** Current native session; access throws after the lease closes or releases. */
|
|
162
|
+
readonly session: BorrowedAgentSession;
|
|
163
|
+
readonly capabilities: SubagentCapabilities;
|
|
164
|
+
subscribe(listener: (event: SubagentLeaseEvent) => void): () => void;
|
|
165
|
+
release(): Promise<void>;
|
|
166
|
+
}
|
|
167
|
+
/** Root-scoped lifecycle authority used by the provider. */
|
|
168
|
+
export interface SubagentOwnerControlPort {
|
|
169
|
+
teardown(path: AgentPath, incarnation: string): Promise<SubagentTeardownResult>;
|
|
170
|
+
stop(request: SubagentStopRequest): Promise<void>;
|
|
171
|
+
resurrect(request: SubagentResurrectRequest): Promise<SubagentResurrectResult>;
|
|
172
|
+
}
|
|
173
|
+
/** Provider-owned leases and guarded lifecycle controls shared by the presentation seam. */
|
|
174
|
+
export interface SubagentProviderControls {
|
|
175
|
+
acquire(path: AgentPath): Promise<SubagentLease>;
|
|
176
|
+
teardown(path: AgentPath, incarnation: string): Promise<SubagentTeardownResult>;
|
|
177
|
+
stop(request: SubagentStopRequest): Promise<void>;
|
|
178
|
+
resurrect(request: SubagentResurrectRequest): Promise<SubagentResurrectResult>;
|
|
179
|
+
}
|
|
180
|
+
/** EventBus request payload used by provider discovery. */
|
|
181
|
+
export type SubagentProviderRequest = Readonly<{
|
|
182
|
+
/** Reserved for future handshake versions. */
|
|
183
|
+
version?: number;
|
|
184
|
+
}>;
|
|
185
|
+
/** EventBus response payload used by provider registration and discovery. */
|
|
186
|
+
export type SubagentProviderResponse = Readonly<{
|
|
187
|
+
provider: import('./presentation.js').SubagentPresentationProvider;
|
|
188
|
+
}>;
|
|
189
|
+
/** A live discovery handle that tracks late registration and replacement. */
|
|
190
|
+
export interface SubagentProviderDiscovery {
|
|
191
|
+
readonly provider: import('./presentation.js').SubagentPresentationProvider | undefined;
|
|
192
|
+
/** Alias for callers that model discovery as a mutable current value. */
|
|
193
|
+
readonly current: import('./presentation.js').SubagentPresentationProvider | undefined;
|
|
194
|
+
subscribe(listener: (provider: import('./presentation.js').SubagentPresentationProvider | undefined) => void): () => void;
|
|
195
|
+
/** Alias for subscribe, useful at the host event boundary. */
|
|
196
|
+
onProvider(listener: (provider: import('./presentation.js').SubagentPresentationProvider | undefined) => void): () => void;
|
|
197
|
+
close(): void;
|
|
198
|
+
}
|
|
199
|
+
/** Extract the EventBus from either ExtensionAPI or an EventBus itself. */
|
|
200
|
+
export type SubagentEventBusOwner = Pick<EventBus, 'on' | 'emit'> | {
|
|
201
|
+
readonly events: Pick<EventBus, 'on' | 'emit'>;
|
|
202
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pimote/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Pimote extensibility SDK: panel cards, background-activity reap guard, and folder discovery
|
|
3
|
+
"version": "0.16.0-rc.2",
|
|
4
|
+
"description": "Pimote extensibility SDK: panel cards, background-activity reap guard, Pimote extensions, and source-only folder discovery",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Antoine Lennartz <alennartz@gmail.com>",
|
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
".": "./dist/index.js",
|
|
27
27
|
"./panels": "./dist/panels/index.js",
|
|
28
28
|
"./activity": "./dist/activity/index.js",
|
|
29
|
-
"./folders": "./dist/folders/index.js"
|
|
29
|
+
"./folders": "./dist/folders/index.js",
|
|
30
|
+
"./pimote": "./dist/pimote/index.js",
|
|
31
|
+
"./subagents": "./dist/subagents/index.js"
|
|
30
32
|
},
|
|
31
33
|
"types": "./dist/index.d.ts",
|
|
32
34
|
"files": [
|
|
@@ -49,7 +51,7 @@
|
|
|
49
51
|
}
|
|
50
52
|
},
|
|
51
53
|
"devDependencies": {
|
|
52
|
-
"@earendil-works/pi-coding-agent": "1.
|
|
54
|
+
"@earendil-works/pi-coding-agent": "1.5.0",
|
|
53
55
|
"typescript": "^6.0.2",
|
|
54
56
|
"vitest": "^4.1.2"
|
|
55
57
|
}
|