@aiwg/cli 2026.9.1 → 2026.9.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.
@@ -489,7 +489,7 @@ providers:
489
489
  agent_teams: false
490
490
  tasks: false
491
491
  mcp: false
492
- behaviors: false
492
+ behaviors: true
493
493
  mission_control: false
494
494
  daemon: false
495
495
  emulation:
@@ -76,6 +76,17 @@
76
76
  "verification": "Inspect active profile and run metadata",
77
77
  "sourceUrl": "https://docs.warp.dev/agent-platform/capabilities/agent-profiles-permissions", "verifiedAt": "2026-07-20"
78
78
  },
79
+ "pi": {
80
+ "agent": "native", "skill": "inherited", "globalChild": "native",
81
+ "identifierSyntax": "provider/model identifier accepted by Pi --model",
82
+ "effortValues": ["minimal", "low", "medium", "high", "xhigh"],
83
+ "inheritance": "Omitted model and thinking level inherit the invoking Pi session",
84
+ "invalidPinFallback": "Pi resolves against its configured catalog and exits on invalid explicit selection",
85
+ "configTarget": "Pi headless launch arguments",
86
+ "artifactFormat": "Runtime --model and --thinking flags",
87
+ "verification": "Inspect strict JSONL state and the selected provider/model",
88
+ "sourceUrl": "https://github.com/earendil-works/pi/tree/main/packages/coding-agent#cli-reference", "verifiedAt": "2026-09-04"
89
+ },
79
90
  "windsurf": {
80
91
  "agent": "unsupported", "skill": "unsupported", "globalChild": "inherited",
81
92
  "identifierSyntax": "UI-selected provider model",
@@ -52,6 +52,14 @@
52
52
  },
53
53
  "sourceUrl": "https://opencode.ai/docs/models", "verifiedAt": "2026-07-20"
54
54
  },
55
+ "pi": {
56
+ "roles": {
57
+ "reasoning": { "id": "configured/reasoning", "status": "unverified", "observed": false },
58
+ "coding": { "id": "configured/coding", "status": "unverified", "observed": false },
59
+ "efficiency": { "id": "configured/efficiency", "status": "unverified", "observed": false }
60
+ },
61
+ "sourceUrl": "https://github.com/earendil-works/pi/tree/main/packages/coding-agent#providers--models", "verifiedAt": "2026-09-04"
62
+ },
55
63
  "warp": {
56
64
  "roles": {
57
65
  "reasoning": { "id": "profile-selected", "status": "unverified", "observed": false },
@@ -0,0 +1,26 @@
1
+ /** AIWG's reviewed Pi bridge. Loaded only after Pi project trust is granted. */
2
+ import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
3
+
4
+ const destructive = /(?:^|[;&|]\s*)(?:sudo\s+)?(?:rm\s+-\S*r\S*\s+|git\s+(?:reset\s+--hard|clean\s+-)|chmod\s+777|chown\s+-R)/i;
5
+ const packageMutation = /(?:^|[;&|]\s*)(?:npm|pnpm|yarn|bun|pipx?|uv|cargo)\s+(?:install|add|update|upgrade)\b/i;
6
+
7
+ export async function evaluateAiwgPiCommand(
8
+ command: string,
9
+ hasUI: boolean,
10
+ confirm?: () => Promise<boolean>,
11
+ ): Promise<{ block: true; reason: string } | undefined> {
12
+ if (!destructive.test(command) && !packageMutation.test(command)) return undefined;
13
+ if (!hasUI) return { block: true, reason: 'AIWG policy blocked a destructive or package-mutating command in headless mode.' };
14
+ return await confirm?.() ? undefined : { block: true, reason: 'Denied by AIWG operator policy.' };
15
+ }
16
+
17
+ export default function aiwgBridge(pi: ExtensionAPI): void {
18
+ pi.on('tool_call', async (event, context) => {
19
+ if (event.toolName !== 'bash') return undefined;
20
+ const command = typeof event.input?.command === 'string' ? event.input.command : '';
21
+ return evaluateAiwgPiCommand(command, context.hasUI, async () => {
22
+ const choice = await context.ui.select('AIWG policy requires explicit approval for this command.', ['Deny', 'Allow once']);
23
+ return choice === 'Allow once';
24
+ });
25
+ });
26
+ }
@@ -17,6 +17,12 @@ export const defaultCommandRunner = (command, args, stdin = "") => new Promise((
17
17
  child.stdout.on("data", (chunk) => collect(stdout, chunk));
18
18
  child.stderr.on("data", (chunk) => collect(stderr, chunk));
19
19
  child.once("error", reject);
20
+ child.stdin.once("error", (error) => {
21
+ // A short-lived credential helper may close stdin before Node flushes the
22
+ // payload. Its process exit remains the authoritative command result.
23
+ if (error.code !== "EPIPE")
24
+ reject(error);
25
+ });
20
26
  child.once("close", (code) => resolve({
21
27
  stdout: Buffer.concat(stdout).toString("utf8"),
22
28
  stderr: Buffer.concat(stderr).toString("utf8"),
@@ -1,7 +1,7 @@
1
1
  import { existsSync, mkdirSync, realpathSync, statSync, } from 'node:fs';
2
2
  import { dirname, isAbsolute, resolve, } from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
- import { CLAUDE_ADAPTER_VERSION, ClaudeSessionAdapter, CODEX_ADAPTER_VERSION, CodexSessionAdapter, COPILOT_ADAPTER_VERSION, CopilotSessionAdapter, CURSOR_ADAPTER_VERSION, CursorSessionAdapter, FACTORY_ADAPTER_VERSION, FactorySessionAdapter, HERMES_ADAPTER_VERSION, HermesSessionAdapter, OPENCODE_ADAPTER_VERSION, OpenCodeSessionAdapter, OPENCLAW_ADAPTER_VERSION, OpenClawSessionAdapter, OPENHUMAN_ADAPTER_VERSION, OpenHumanSessionAdapter, WARP_ADAPTER_VERSION, WarpSessionAdapter, DEVIN_DESKTOP_ADAPTER_VERSION, DevinDesktopSessionAdapter, CandidateExtractionService, GENERIC_ADAPTER_VERSION, GenericSessionInterchangeAdapter, IncrementalSessionImporter, ImportLeaseContentionError, FilesystemMemoryDestination, FilesystemPromotionDispositionCoordinator, MemoryPromotionGateway, SESSION_CONTRACT_VERSION, SESSION_PROVIDER_IDS, SessionContractError, SessionRepository, SessionSourceSchema, StructuralCandidateExtractor, resolveMemoryConsumerManifest, assertSessionProviderId, acquireImportLease, defaultDiscoveryManifestPath, discoverWorkspaceHistories, deriveSessionTimeline, importDiscoveryManifest, previewDiscoveryImport, publicDiscoveryManifest, readDiscoveryManifest, redactSourceLocator, sha256, parseTimelineGap, writeDiscoveryManifest, } from '../../sessions/index.js';
4
+ import { CLAUDE_ADAPTER_VERSION, ClaudeSessionAdapter, CODEX_ADAPTER_VERSION, CodexSessionAdapter, COPILOT_ADAPTER_VERSION, CopilotSessionAdapter, CURSOR_ADAPTER_VERSION, CursorSessionAdapter, FACTORY_ADAPTER_VERSION, FactorySessionAdapter, HERMES_ADAPTER_VERSION, HermesSessionAdapter, OPENCODE_ADAPTER_VERSION, OpenCodeSessionAdapter, OPENCLAW_ADAPTER_VERSION, OpenClawSessionAdapter, OPENHUMAN_ADAPTER_VERSION, OpenHumanSessionAdapter, PI_ADAPTER_VERSION, PiSessionAdapter, WARP_ADAPTER_VERSION, WarpSessionAdapter, DEVIN_DESKTOP_ADAPTER_VERSION, DevinDesktopSessionAdapter, CandidateExtractionService, GENERIC_ADAPTER_VERSION, GenericSessionInterchangeAdapter, IncrementalSessionImporter, ImportLeaseContentionError, FilesystemMemoryDestination, FilesystemPromotionDispositionCoordinator, MemoryPromotionGateway, SESSION_CONTRACT_VERSION, SESSION_PROVIDER_IDS, SessionContractError, SessionRepository, SessionSourceSchema, StructuralCandidateExtractor, resolveMemoryConsumerManifest, assertSessionProviderId, acquireImportLease, defaultDiscoveryManifestPath, discoverWorkspaceHistories, deriveSessionTimeline, importDiscoveryManifest, previewDiscoveryImport, publicDiscoveryManifest, readDiscoveryManifest, redactSourceLocator, sha256, parseTimelineGap, writeDiscoveryManifest, } from '../../sessions/index.js';
5
5
  const JSON_CONTRACT_VERSION = '1.0.0';
6
6
  async function createLineMemoryPromotionDestination(projectRoot, manifestPath) {
7
7
  const modulePath = resolve(dirname(manifestPath), 'commands', 'line-memory.mjs');
@@ -622,7 +622,7 @@ async function importSource(ctx, args) {
622
622
  if (provider !== 'generic' && provider !== 'claude' && provider !== 'codex'
623
623
  && provider !== 'copilot' && provider !== 'cursor' && provider !== 'factory'
624
624
  && provider !== 'hermes' && provider !== 'opencode' && provider !== 'openclaw'
625
- && provider !== 'openhuman' && provider !== 'warp' && provider !== 'devin-desktop') {
625
+ && provider !== 'openhuman' && provider !== 'pi' && provider !== 'warp' && provider !== 'devin-desktop') {
626
626
  throw new CliError('UNSUPPORTED_OPERATION', `session import is not implemented for ${provider}`, EXIT.unsupported);
627
627
  }
628
628
  const sourceId = requiredValue(args, '--source-id');
@@ -638,6 +638,7 @@ async function importSource(ctx, args) {
638
638
  const isOpenCode = provider === 'opencode';
639
639
  const isOpenClaw = provider === 'openclaw';
640
640
  const isOpenHuman = provider === 'openhuman';
641
+ const isPi = provider === 'pi';
641
642
  const isWarp = provider === 'warp';
642
643
  const isDevinDesktop = provider === 'devin-desktop';
643
644
  const adapter = isClaude
@@ -658,11 +659,13 @@ async function importSource(ctx, args) {
658
659
  ? new OpenClawSessionAdapter()
659
660
  : isOpenHuman
660
661
  ? new OpenHumanSessionAdapter()
661
- : isWarp
662
- ? new WarpSessionAdapter()
663
- : isDevinDesktop
664
- ? new DevinDesktopSessionAdapter()
665
- : new GenericSessionInterchangeAdapter();
662
+ : isPi
663
+ ? new PiSessionAdapter()
664
+ : isWarp
665
+ ? new WarpSessionAdapter()
666
+ : isDevinDesktop
667
+ ? new DevinDesktopSessionAdapter()
668
+ : new GenericSessionInterchangeAdapter();
666
669
  const locatorClass = isClaude
667
670
  ? (input.endsWith('.hooks.jsonl') ? 'claude-hook-jsonl' : 'claude-transcript-jsonl')
668
671
  : isCodex
@@ -681,11 +684,13 @@ async function importSource(ctx, args) {
681
684
  ? 'openclaw-consistent-snapshot-jsonl'
682
685
  : isOpenHuman
683
686
  ? 'openhuman-enriched-jsonl'
684
- : isWarp
685
- ? 'warp-markdown-export'
686
- : isDevinDesktop
687
- ? 'devin-desktop-cascade-hook-jsonl'
688
- : 'manual-export';
687
+ : isPi
688
+ ? 'pi-session-v3-jsonl'
689
+ : isWarp
690
+ ? 'warp-markdown-export'
691
+ : isDevinDesktop
692
+ ? 'devin-desktop-cascade-hook-jsonl'
693
+ : 'manual-export';
689
694
  const selectedSource = {
690
695
  provider, locator: input, locatorClass, sourceId,
691
696
  authorizedScope: { workspaceId, allowedRoots: [dirname(input)] },
@@ -1007,6 +1012,19 @@ function providerDisposition(provider) {
1007
1012
  },
1008
1013
  };
1009
1014
  }
1015
+ if (provider === 'pi') {
1016
+ return {
1017
+ provider, disposition: 'implemented', operationalState: 'available',
1018
+ supportedOperations: ['discover', 'inspect', 'stream'],
1019
+ acquisitionModes: ['jsonl'], reasonCode: null,
1020
+ remediation: 'Authorize PI_CODING_AGENT_SESSION_DIR, the default Pi sessions root, or an explicit v3 JSONL export.',
1021
+ evidence: {
1022
+ adapterVersion: PI_ADAPTER_VERSION,
1023
+ verifiedAt: '2026-09-04',
1024
+ documentation: 'https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/core/session-manager.ts',
1025
+ },
1026
+ };
1027
+ }
1010
1028
  if (provider === 'warp') {
1011
1029
  return {
1012
1030
  provider, disposition: 'manual-only', operationalState: 'available',
@@ -76,6 +76,17 @@
76
76
  "verification": "Inspect active profile and run metadata",
77
77
  "sourceUrl": "https://docs.warp.dev/agent-platform/capabilities/agent-profiles-permissions", "verifiedAt": "2026-07-20"
78
78
  },
79
+ "pi": {
80
+ "agent": "native", "skill": "inherited", "globalChild": "native",
81
+ "identifierSyntax": "provider/model identifier accepted by Pi --model",
82
+ "effortValues": ["minimal", "low", "medium", "high", "xhigh"],
83
+ "inheritance": "Omitted model and thinking level inherit the invoking Pi session",
84
+ "invalidPinFallback": "Pi resolves against its configured catalog and exits on invalid explicit selection",
85
+ "configTarget": "Pi headless launch arguments",
86
+ "artifactFormat": "Runtime --model and --thinking flags",
87
+ "verification": "Inspect strict JSONL state and the selected provider/model",
88
+ "sourceUrl": "https://github.com/earendil-works/pi/tree/main/packages/coding-agent#cli-reference", "verifiedAt": "2026-09-04"
89
+ },
79
90
  "windsurf": {
80
91
  "agent": "unsupported", "skill": "unsupported", "globalChild": "inherited",
81
92
  "identifierSyntax": "UI-selected provider model",
@@ -52,6 +52,14 @@
52
52
  },
53
53
  "sourceUrl": "https://opencode.ai/docs/models", "verifiedAt": "2026-07-20"
54
54
  },
55
+ "pi": {
56
+ "roles": {
57
+ "reasoning": { "id": "configured/reasoning", "status": "unverified", "observed": false },
58
+ "coding": { "id": "configured/coding", "status": "unverified", "observed": false },
59
+ "efficiency": { "id": "configured/efficiency", "status": "unverified", "observed": false }
60
+ },
61
+ "sourceUrl": "https://github.com/earendil-works/pi/tree/main/packages/coding-agent#providers--models", "verifiedAt": "2026-09-04"
62
+ },
55
63
  "warp": {
56
64
  "roles": {
57
65
  "reasoning": { "id": "profile-selected", "status": "unverified", "observed": false },
@@ -67,6 +67,13 @@ export const PROVIDER_DISCOVERY_DECISIONS = {
67
67
  reason: 'OpenHuman profiles accept semantic model hints but expose no standardized local model-list command.',
68
68
  documentation: 'https://github.com/roctinam/openhuman',
69
69
  },
70
+ pi: {
71
+ provider: 'pi',
72
+ status: 'native',
73
+ interface: 'pi --list-models',
74
+ reason: 'Pi exposes the configured provider/model catalog through a read-only non-interactive table.',
75
+ documentation: 'https://github.com/earendil-works/pi/tree/main/packages/coding-agent#cli-reference',
76
+ },
70
77
  warp: {
71
78
  provider: 'warp',
72
79
  status: 'unsupported',
@@ -161,6 +168,30 @@ export async function discoverOpenCodeModels(command = 'opencode', runner = runM
161
168
  : {}),
162
169
  };
163
170
  }
171
+ export async function discoverPiModels(command = 'pi', runner = runModelDiscoveryCommand) {
172
+ const observedAt = new Date().toISOString();
173
+ const [version, result] = await Promise.all([
174
+ runtimeVersion(command, runner),
175
+ runner(command, ['--list-models'], { cwd: tmpdir(), timeoutMs: 15_000 }),
176
+ ]);
177
+ if (result.exitCode !== 0) {
178
+ const error = result.stderr.trim() || `Pi --list-models exited ${result.exitCode}`;
179
+ return { provider: 'pi', source: 'native', observedAt,
180
+ ...(version ? { runtimeVersion: version } : {}), accountScope: 'local-runtime',
181
+ models: [], errorKind: classifyDiscoveryError(error), error };
182
+ }
183
+ const lines = result.stdout.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
184
+ const models = lines.slice(1).flatMap(line => {
185
+ const columns = line.split(/\s{2,}/);
186
+ if (columns.length < 2 || !/^[a-z0-9][a-z0-9._-]*$/i.test(columns[0]))
187
+ return [];
188
+ return [{ id: `${columns[0]}/${columns[1]}` }];
189
+ });
190
+ return { provider: 'pi', source: 'native', observedAt,
191
+ ...(version ? { runtimeVersion: version } : {}), accountScope: 'local-runtime', models,
192
+ ...(models.length === 0 ? { errorKind: 'invalid-output',
193
+ error: 'Pi returned no parseable provider/model rows.' } : {}) };
194
+ }
164
195
  export async function discoverOpenClawModels(command = 'openclaw', runner = runModelDiscoveryCommand) {
165
196
  const observedAt = new Date().toISOString();
166
197
  const [version, result] = await Promise.all([
@@ -455,6 +486,7 @@ export async function resolveDynamicModelCatalog(options) {
455
486
  codex: () => discoverCodexModels(),
456
487
  opencode: () => discoverOpenCodeModels(),
457
488
  openclaw: () => discoverOpenClawModels(),
489
+ pi: () => discoverPiModels(),
458
490
  };
459
491
  const providerDiscovery = {};
460
492
  for (const provider of available) {
@@ -27,7 +27,7 @@ const capabilityData = requireModelResource('model-capabilities.v1.json');
27
27
  const catalogData = requireModelResource('model-catalog.v1.json');
28
28
  const ProviderSchema = z.enum([
29
29
  'claude', 'codex', 'copilot', 'cursor', 'factory', 'hermes',
30
- 'opencode', 'openclaw', 'openhuman', 'warp', 'windsurf',
30
+ 'opencode', 'openclaw', 'openhuman', 'pi', 'warp', 'windsurf',
31
31
  ]);
32
32
  const OutcomeSchema = z.enum([
33
33
  'native', 'compiled', 'inherited', 'global-only', 'informational', 'unsupported',
@@ -107,7 +107,7 @@ export function loadProviderModelCapabilities() {
107
107
  const expected = new Set(ProviderSchema.options);
108
108
  const actual = new Set(Object.keys(registryCache.providers));
109
109
  if (actual.size !== expected.size || [...expected].some(id => !actual.has(id))) {
110
- throw new Error('Provider model capability registry must cover all 11 providers');
110
+ throw new Error('Provider model capability registry must cover all 12 providers');
111
111
  }
112
112
  }
113
113
  return registryCache;
@@ -159,6 +159,7 @@ function fieldNames(provider) {
159
159
  case 'copilot':
160
160
  case 'cursor':
161
161
  case 'opencode': return { model: 'model' };
162
+ case 'pi': return { model: 'model', effort: 'thinking' };
162
163
  case 'openhuman': return { model: 'model_hint' };
163
164
  case 'openclaw': return { model: 'subagents.model' };
164
165
  default: return {};
@@ -489,7 +489,7 @@ providers:
489
489
  agent_teams: false
490
490
  tasks: false
491
491
  mcp: false
492
- behaviors: false
492
+ behaviors: true
493
493
  mission_control: false
494
494
  daemon: false
495
495
  emulation:
@@ -0,0 +1,141 @@
1
+ import { opendir } from 'node:fs/promises';
2
+ import { basename, resolve } from 'node:path';
3
+ import { z } from 'zod';
4
+ import { SessionContractError, } from '../contracts.js';
5
+ import { readBoundedJsonLines, streamBoundedJsonLines } from '../readers.js';
6
+ export const PI_ADAPTER_VERSION = '1.0.0';
7
+ export const PI_SOURCE_SCHEMA_VERSION = '3.0.0';
8
+ const Header = z.object({ type: z.literal('session'), version: z.number().int(), id: z.string().min(1),
9
+ timestamp: z.string().datetime({ offset: true }), cwd: z.string(), parentSession: z.string().optional() }).passthrough();
10
+ const Entry = z.object({ type: z.string().min(1), id: z.string().min(1),
11
+ parentId: z.string().nullable(), timestamp: z.string().datetime({ offset: true }) }).passthrough();
12
+ export class PiSessionAdapter {
13
+ limits;
14
+ maxFiles;
15
+ provider = 'pi';
16
+ adapterVersion = PI_ADAPTER_VERSION;
17
+ disposition = 'implemented';
18
+ supportedOperations = ['discover', 'inspect', 'stream'];
19
+ acquisitionModes = ['jsonl'];
20
+ constructor(limits, maxFiles = 10_000) {
21
+ this.limits = limits;
22
+ this.maxFiles = maxFiles;
23
+ }
24
+ async *discover(scope) {
25
+ if (!scope.allowedRoots.length)
26
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'Pi discovery requires an authorized sessions root');
27
+ let count = 0;
28
+ for (const root of [...scope.allowedRoots].sort()) {
29
+ for await (const locator of jsonlFiles(resolve(root))) {
30
+ if (++count > this.maxFiles)
31
+ throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Pi discovery file limit exceeded');
32
+ yield { provider: 'pi', locator, locatorClass: 'pi-session-v3-jsonl' };
33
+ }
34
+ }
35
+ }
36
+ async inspect(source) {
37
+ let input;
38
+ try {
39
+ input = await readBoundedJsonLines({ selectedPath: source.locator,
40
+ allowedRoots: source.authorizedScope.allowedRoots }, { consistency: 'provisional', limits: this.limits });
41
+ }
42
+ catch (error) {
43
+ throw normalizeAuthorizationError(error);
44
+ }
45
+ const header = parseHeader(input.records[0]?.value);
46
+ if (input.incompleteTail)
47
+ throw new SessionContractError('TRUNCATED_SOURCE', 'Pi session has a truncated JSONL tail');
48
+ return { sourceSchemaVersion: `${header.version}.0.0`, consistency: 'complete', operationalState: 'available' };
49
+ }
50
+ async *stream(source, cursor) {
51
+ let input;
52
+ try {
53
+ input = await streamBoundedJsonLines({ selectedPath: source.locator,
54
+ allowedRoots: source.authorizedScope.allowedRoots }, { cursor: cursor?.value, consistency: 'provisional', limits: this.limits });
55
+ }
56
+ catch (error) {
57
+ throw normalizeAuthorizationError(error);
58
+ }
59
+ let sessionId = '';
60
+ let index = 0;
61
+ const ids = new Set();
62
+ for await (const line of input) {
63
+ if (index++ === 0 && !cursor) {
64
+ sessionId = parseHeader(line.value).id;
65
+ continue;
66
+ }
67
+ if (!sessionId)
68
+ sessionId = basename(source.locator, '.jsonl');
69
+ const parsed = Entry.safeParse(line.value);
70
+ if (!parsed.success)
71
+ throw new SessionContractError('MALFORMED_SOURCE', 'Pi session entry is malformed');
72
+ const entry = parsed.data;
73
+ if (ids.has(entry.id))
74
+ throw new SessionContractError('DUPLICATE_NATIVE_ID', 'Pi session contains a duplicate entry id');
75
+ ids.add(entry.id);
76
+ const message = object(entry.message);
77
+ const role = string(message.role) ?? roleFor(entry.type);
78
+ const sensitive = role === 'toolResult' || entry.type === 'custom';
79
+ const text = sensitive ? '[redacted provider content]' : entryText(entry, message);
80
+ yield { nativeSessionId: sessionId, nativeEventId: entry.id, sequence: line.sequence,
81
+ kind: kindFor(entry.type, role), role, toolName: sensitive ? string(message.toolName) : undefined,
82
+ toolCallId: sensitive ? string(message.toolCallId) : undefined,
83
+ occurredAt: entry.timestamp, text, sourceCursor: String(line.byteOffset + line.byteLength),
84
+ sourceBytes: line.byteLength, rawReference: { locatorClass: 'pi-session-v3-jsonl', offset: line.byteOffset },
85
+ activityBoundary: entry.type === 'compaction' ? 'continuation' : undefined,
86
+ activityBoundaryBasis: entry.type === 'compaction' ? 'pi-session:compaction' : undefined,
87
+ activityBoundaryConfidence: entry.type === 'compaction' ? 'high' : undefined,
88
+ extensions: { provenance: { acquisition: 'pi-session-v3', parentId: entry.parentId },
89
+ opaque: !KNOWN.has(entry.type), redacted: sensitive,
90
+ nativeType: entry.type, parentId: entry.parentId } };
91
+ }
92
+ if (input.incompleteTail)
93
+ throw new SessionContractError('TRUNCATED_SOURCE', 'Pi session has a truncated JSONL tail');
94
+ }
95
+ }
96
+ const KNOWN = new Set(['message', 'thinking_level_change', 'model_change', 'compaction',
97
+ 'branch_summary', 'custom', 'label', 'session_info', 'custom_message']);
98
+ function parseHeader(value) {
99
+ const parsed = Header.safeParse(value);
100
+ if (!parsed.success)
101
+ throw new SessionContractError('MALFORMED_SOURCE', 'Pi session header is malformed');
102
+ if (parsed.data.version !== 3)
103
+ throw new SessionContractError('UNKNOWN_SCHEMA_MAJOR', `unsupported Pi session version: ${parsed.data.version}`);
104
+ return parsed.data;
105
+ }
106
+ function object(value) { return value && typeof value === 'object' ? value : {}; }
107
+ function string(value) { return typeof value === 'string' && value ? value : undefined; }
108
+ function roleFor(type) { return type === 'compaction' || type === 'branch_summary' ? 'system' : undefined; }
109
+ function kindFor(type, role) { return type === 'message' ? `message.${role ?? 'unknown'}` : `pi.${type}`; }
110
+ function entryText(entry, message) {
111
+ const value = entry.type === 'message' ? message.content : entry.summary ?? entry.name ?? entry.label ?? '';
112
+ if (typeof value === 'string')
113
+ return value;
114
+ if (Array.isArray(value))
115
+ return value.flatMap(part => typeof part === 'string' ? part : string(object(part).text) ?? []).join('\n');
116
+ return '';
117
+ }
118
+ async function* jsonlFiles(root) {
119
+ let directory;
120
+ try {
121
+ directory = await opendir(root);
122
+ }
123
+ catch {
124
+ return;
125
+ }
126
+ for await (const entry of directory) {
127
+ const path = resolve(root, entry.name);
128
+ if (entry.isDirectory())
129
+ yield* jsonlFiles(path);
130
+ else if (entry.isFile() && entry.name.endsWith('.jsonl'))
131
+ yield path;
132
+ }
133
+ }
134
+ function normalizeAuthorizationError(error) {
135
+ if (error instanceof SessionContractError && (error.code === 'SOURCE_OUTSIDE_ALLOWED_ROOT'
136
+ || error.code === 'SOURCE_SYMLINK' || error.code === 'SOURCE_NOT_REGULAR_FILE')) {
137
+ return new SessionContractError('SOURCE_NOT_AUTHORIZED', 'Pi source is not an authorized regular file');
138
+ }
139
+ return error;
140
+ }
141
+ //# sourceMappingURL=pi.js.map
@@ -3,7 +3,7 @@ import { z } from 'zod';
3
3
  export const SESSION_CONTRACT_VERSION = '1.0.0';
4
4
  export const SESSION_PROVIDER_IDS = [
5
5
  'claude', 'codex', 'copilot', 'cursor', 'factory', 'hermes',
6
- 'opencode', 'openclaw', 'openhuman', 'warp', 'devin-desktop', 'generic',
6
+ 'opencode', 'openclaw', 'openhuman', 'pi', 'warp', 'devin-desktop', 'generic',
7
7
  ];
8
8
  export const SessionProviderIdSchema = z.enum(SESSION_PROVIDER_IDS);
9
9
  export const SESSION_PROVIDER_ALIASES = Object.freeze({
@@ -28,6 +28,7 @@ export * from './adapters/hermes.js';
28
28
  export * from './adapters/opencode.js';
29
29
  export * from './adapters/openclaw.js';
30
30
  export * from './adapters/openhuman.js';
31
+ export * from './adapters/pi.js';
31
32
  export * from './adapters/warp.js';
32
33
  export * from './adapters/windsurf.js';
33
34
  //# sourceMappingURL=index.js.map
@@ -7,6 +7,7 @@ import { ClaudeSessionAdapter } from './adapters/claude.js';
7
7
  import { CodexSessionAdapter } from './adapters/codex.js';
8
8
  import { CursorSessionAdapter } from './adapters/cursor.js';
9
9
  import { FactorySessionAdapter } from './adapters/factory.js';
10
+ import { PiSessionAdapter } from './adapters/pi.js';
10
11
  import { SESSION_PROVIDER_IDS, sha256, } from './contracts.js';
11
12
  import { redactSourceLocator } from './discovery.js';
12
13
  import { fingerprintSourceFile } from './readers.js';
@@ -49,6 +50,15 @@ export async function discoverWorkspaceHistories(options) {
49
50
  join(providerHome, '.factory', 'sessions', keyWithLeadingDash),
50
51
  ]),
51
52
  },
53
+ {
54
+ provider: 'pi',
55
+ adapter: new PiSessionAdapter(),
56
+ roots: options.providerHome
57
+ ? [process.env.PI_CODING_AGENT_SESSION_DIR
58
+ ? resolve(process.env.PI_CODING_AGENT_SESSION_DIR)
59
+ : join(resolve(options.providerHome), '.pi', 'agent', 'sessions')]
60
+ : [],
61
+ },
52
62
  ];
53
63
  const reports = new Map();
54
64
  const candidates = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwg/cli",
3
- "version": "2026.9.1",
3
+ "version": "2026.9.2",
4
4
  "description": "Lightweight AIWG CLI for signed, versioned web-backed resources.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import path from 'path';
11
+ import fs from 'fs';
11
12
  import {
12
13
  collectFrameworkArtifacts,
13
14
  createAgentsMdFromTemplate,
@@ -31,6 +32,8 @@ export const paths = {
31
32
  rules: '',
32
33
  };
33
34
 
35
+ export const extensionBridge = 'agentic/code/providers/pi/aiwg-bridge.ts';
36
+
34
37
  export const kernelSkillsPath = '.agents/skills';
35
38
 
36
39
  export const support = {
@@ -109,6 +112,14 @@ export function deployRules() {
109
112
  return 0;
110
113
  }
111
114
 
115
+ export function deployExtensionBridge(targetDir, opts) {
116
+ const source = path.join(resolveAiwgRoot(opts.srcRoot) || opts.srcRoot, extensionBridge);
117
+ if (!fs.existsSync(source)) return 0;
118
+ const destination = path.join(targetDir, '.pi/extensions');
119
+ ensureDir(destination, opts.dryRun);
120
+ return deployFiles([source], destination, opts);
121
+ }
122
+
112
123
  export function createAgentsMd(target, srcRoot, dryRun) {
113
124
  const aiwgRoot = resolveAiwgRoot(srcRoot) || srcRoot;
114
125
  createAgentsMdFromTemplate(target, aiwgRoot, 'pi/AGENTS.md.aiwg-template', dryRun);
@@ -153,6 +164,7 @@ export async function deploy(opts) {
153
164
  if (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly) count += deployAgents(agentFiles, opts.target, opts);
154
165
  if ((opts.deployCommands || opts.commandsOnly) && !opts.skillsOnly && !opts.rulesOnly) count += deployCommands(commandFiles, opts.target, opts);
155
166
  if ((opts.deploySkills || opts.skillsOnly) && !opts.commandsOnly && !opts.rulesOnly) count += deploySkills(skillDirs, opts.target, opts);
167
+ if (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly) count += deployExtensionBridge(opts.target, opts);
156
168
  await postDeploy(opts.target, opts);
157
169
  return count;
158
170
  }
@@ -160,5 +172,5 @@ export async function deploy(opts) {
160
172
  export default {
161
173
  name, aliases, paths, kernelSkillsPath, support, capabilities,
162
174
  mapModel, transformAgent, transformCommand, deployAgents, deployCommands,
163
- deploySkills, deployRules, createAgentsMd, postDeploy, getFileExtension, deploy,
175
+ deploySkills, deployRules, deployExtensionBridge, createAgentsMd, postDeploy, getFileExtension, deploy,
164
176
  };