@akagilnc/pi-workflow-roles 0.1.2521 → 0.1.2653-next.71811ca
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 +129 -39
- package/README.zh-CN.md +128 -39
- package/dist/activation-ledger-topology.js +1 -1
- package/dist/archivist-record-entry.js +47 -26
- package/dist/auditor-dossier-tool.js +3 -1
- package/dist/compliance-transport.js +17 -26
- package/dist/doctor-contracts.js +1 -2
- package/dist/dossier-resolution.js +4 -8
- package/dist/engine-detour-tool.js +33 -8
- package/dist/engine-detour.js +1 -0
- package/dist/evidence-child-executor.js +277 -424
- package/dist/host-contracts.js +22 -0
- package/dist/institutional-resolution.js +88 -0
- package/dist/merger-contracts.js +1 -2
- package/dist/navigator-attendance.js +16 -0
- package/dist/notary-contracts.js +2 -3
- package/dist/package-contracts/fixer-output.js +1 -2
- package/dist/packaged-role-registry.js +102 -9
- package/dist/pi/in-process-session.js +671 -0
- package/dist/public-cli/config.js +7 -5
- package/dist/public-cli/main.js +3767 -4113
- package/dist/reviewer-child-executor.js +6 -1
- package/dist/session-opening-materials.js +10 -38
- package/dist/sitian-appender.js +146 -0
- package/dist/sitian-contracts.js +21 -0
- package/dist/sitian-facade.js +15 -0
- package/dist/sitian-reader.js +52 -0
- package/extensions/role-runtime.ts +9 -7
- package/package.json +5 -5
- package/resources/engines/cursor.md +12 -26
- package/resources/engines/hermes.md +2 -23
- package/resources/engines/opus.md +1 -16
- package/resources/navigator-route-playbook.md +2 -2
- package/scripts/build-package.mjs +0 -1
- package/souls/quality-law.md +1 -1
- package/src/activation-ledger-topology.ts +1 -1
- package/src/archivist-record-entry.ts +57 -45
- package/src/auditor-dossier-tool.ts +4 -2
- package/src/canonical-skill-binding.ts +37 -27
- package/src/collector-role.ts +11 -20
- package/src/collector-tool-schemas.ts +1 -4
- package/src/compliance-transport.ts +19 -44
- package/src/doctor-auditor.ts +1 -5
- package/src/doctor-contracts.ts +1 -4
- package/src/doctor-role.ts +31 -8
- package/src/dossier-resolution.ts +8 -8
- package/src/engine-detour-tool.ts +48 -19
- package/src/engine-detour.ts +3 -0
- package/src/evidence-child-executor.ts +277 -485
- package/src/factory-board.ts +3 -3
- package/src/gatekeeper-role.ts +21 -10
- package/src/host-contracts.ts +386 -0
- package/src/in-process-session.ts +1 -115
- package/src/institutional-resolution.ts +135 -0
- package/src/judge-auditor.ts +1 -5
- package/src/judge-role.ts +37 -51
- package/src/merger-contracts.ts +1 -4
- package/src/merger-role.ts +7 -11
- package/src/navigator-attendance.ts +20 -1
- package/src/notary-contracts.ts +8 -11
- package/src/notary-role.ts +7 -23
- package/src/notary-source-run.ts +4 -3
- package/src/package-contracts/fixer-output.ts +1 -4
- package/src/package-resources/method-skill-binding.ts +10 -34
- package/src/packaged-role-registry.ts +122 -12
- package/src/pi/adapter.ts +192 -0
- package/src/pi/durable-principal.ts +98 -0
- package/src/pi/in-process-session.ts +819 -0
- package/src/pi/known-failure.ts +77 -0
- package/src/pi/pi-normalization.ts +284 -0
- package/src/pi/role-turn-host.ts +459 -0
- package/src/public-cli/auto-resume.ts +42 -44
- package/src/public-cli/cli.ts +171 -257
- package/src/public-cli/coder-run.ts +115 -433
- package/src/public-cli/collector-run.ts +62 -314
- package/src/public-cli/config.ts +7 -5
- package/src/public-cli/doctor-run.ts +41 -52
- package/src/public-cli/fixer-run.ts +122 -427
- package/src/public-cli/invocation.ts +287 -96
- package/src/public-cli/judge-run.ts +92 -422
- package/src/public-cli/merger-run.ts +167 -429
- package/src/public-cli/notary-run.ts +41 -47
- package/src/public-cli/option-definitions.ts +136 -3
- package/src/public-cli/post-admission.ts +497 -0
- package/src/public-cli/public-run-credentials.ts +4 -4
- package/src/public-cli/reviewer-dispatch-rejection.ts +106 -0
- package/src/public-cli/reviewer-run.ts +132 -449
- package/src/public-cli/run-lifecycle.ts +339 -327
- package/src/public-cli/settlement.ts +251 -134
- package/src/public-cli/terminal.ts +3 -9
- package/src/public-cli/turn-request.ts +87 -0
- package/src/reviewer-child-executor.ts +9 -1
- package/src/reviewer-role.ts +11 -20
- package/src/role-runtime.ts +129 -78
- package/src/session-opening-materials.ts +25 -39
- package/src/sitian-appender.ts +171 -0
- package/src/sitian-contracts.ts +106 -0
- package/src/sitian-facade.ts +18 -0
- package/src/sitian-reader.ts +62 -0
- package/src/worker-role.ts +42 -40
- package/src/worker-submission-gates.ts +33 -0
- package/dist/archivist-role-run-coordinates.js +0 -28
- package/dist/package-contracts/terminating-infrastructure.js +0 -90
- package/resources/engines/sonnet.md +0 -27
- package/resources/engines/zcode.md +0 -63
- package/src/archivist-role-run-coordinates.ts +0 -45
- package/src/package-contracts/terminating-infrastructure.ts +0 -132
- package/src/public-cli/explicit-internal.ts +0 -427
- package/src/public-cli/one-shot-dispatch.ts +0 -300
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Composition-root unique authoritative public-role records (#509 / #524). */
|
|
2
2
|
import { COLLECTOR_OUTPUT_TOOL } from "./package-contracts/collector-output.ts";
|
|
3
3
|
import { JUDGE_OUTPUT_TOOL_NAME } from "./package-contracts/judge-output.ts";
|
|
4
4
|
import { REVIEWER_OUTPUT_TOOL_NAME } from "./package-contracts/reviewer-output.ts";
|
|
@@ -7,20 +7,130 @@ import { DOCTOR_OUTPUT_TOOL_NAME } from "./doctor-contracts.ts";
|
|
|
7
7
|
import { MERGER_OUTPUT_TOOL_NAME } from "./merger-contracts.ts";
|
|
8
8
|
import { NOTARY_OUTPUT_TOOL_NAME } from "./notary-contracts.ts";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
/** Shared by public notary and gatekeeper-province notary. */
|
|
11
|
+
export const NOTARY_SESSION_MATERIALS = [
|
|
12
|
+
"CLAUDE.md",
|
|
13
|
+
"souls/notary.md",
|
|
14
|
+
"souls/gate-output-guide.md",
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* One record per public callable role. Navigator is absent (name-only materials
|
|
19
|
+
* live on the session-opening projection).
|
|
20
|
+
*/
|
|
21
|
+
export const PUBLIC_ROLE_RECORDS = [
|
|
22
|
+
{
|
|
23
|
+
role: "judge",
|
|
24
|
+
phases: [null],
|
|
25
|
+
outputTool: JUDGE_OUTPUT_TOOL_NAME,
|
|
26
|
+
inputFlag: undefined,
|
|
27
|
+
phaseFlag: undefined,
|
|
28
|
+
activationStage: "load-and-install",
|
|
29
|
+
sessionMaterials: [
|
|
30
|
+
"CLAUDE.md",
|
|
31
|
+
"souls/judge.md",
|
|
32
|
+
"souls/audit-law.md",
|
|
33
|
+
"souls/quality-law.md",
|
|
34
|
+
"souls/judge-output-guide.md",
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
role: "fixer",
|
|
39
|
+
phases: ["plan", "apply"],
|
|
40
|
+
outputTool: FIXER_OUTPUT_TOOL_NAME,
|
|
41
|
+
inputFlag: "ak-fix-packet",
|
|
42
|
+
phaseFlag: "ak-fixer-phase",
|
|
43
|
+
activationStage: "load-and-install",
|
|
44
|
+
sessionMaterials: [
|
|
45
|
+
"CLAUDE.md",
|
|
46
|
+
"souls/fixer.md",
|
|
47
|
+
"souls/quality-law.md",
|
|
48
|
+
"souls/fixer-output-guide.md",
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
role: "coder",
|
|
53
|
+
phases: ["plan", "apply"],
|
|
54
|
+
outputTool: CODER_OUTPUT_TOOL_NAME,
|
|
55
|
+
inputFlag: "ak-coder-task",
|
|
56
|
+
phaseFlag: "ak-coder-phase",
|
|
57
|
+
activationStage: "load-and-install",
|
|
58
|
+
sessionMaterials: [
|
|
59
|
+
"CLAUDE.md",
|
|
60
|
+
"souls/coder.md",
|
|
61
|
+
"souls/quality-law.md",
|
|
62
|
+
"souls/coder-output-guide.md",
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
role: "reviewer",
|
|
67
|
+
phases: [null],
|
|
68
|
+
outputTool: REVIEWER_OUTPUT_TOOL_NAME,
|
|
69
|
+
inputFlag: undefined,
|
|
70
|
+
phaseFlag: undefined,
|
|
71
|
+
activationStage: "load-and-install",
|
|
72
|
+
sessionMaterials: [
|
|
73
|
+
"CLAUDE.md",
|
|
74
|
+
"souls/reviewer.md",
|
|
75
|
+
"souls/audit-law.md",
|
|
76
|
+
"souls/quality-law.md",
|
|
77
|
+
],
|
|
78
|
+
},
|
|
15
79
|
// ak-collector-repo is GitHub owner/repo identity, not a local material path (#438).
|
|
16
|
-
{
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
80
|
+
{
|
|
81
|
+
role: "collector",
|
|
82
|
+
phases: [null],
|
|
83
|
+
outputTool: COLLECTOR_OUTPUT_TOOL,
|
|
84
|
+
inputFlag: undefined,
|
|
85
|
+
phaseFlag: undefined,
|
|
86
|
+
activationStage: "load-and-install",
|
|
87
|
+
sessionMaterials: ["CLAUDE.md", "souls/collector.md"],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
role: "doctor",
|
|
91
|
+
phases: [null],
|
|
92
|
+
outputTool: DOCTOR_OUTPUT_TOOL_NAME,
|
|
93
|
+
inputFlag: "ak-doctor-case",
|
|
94
|
+
phaseFlag: undefined,
|
|
95
|
+
activationStage: "load-and-install",
|
|
96
|
+
sessionMaterials: ["CLAUDE.md", "souls/doctor.md"],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
role: "merger",
|
|
100
|
+
phases: [null],
|
|
101
|
+
outputTool: MERGER_OUTPUT_TOOL_NAME,
|
|
102
|
+
inputFlag: "ak-merger-input",
|
|
103
|
+
phaseFlag: undefined,
|
|
104
|
+
activationStage: "prepare-git-and-install",
|
|
105
|
+
sessionMaterials: ["CLAUDE.md", "souls/merger.md"],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
role: "notary",
|
|
109
|
+
phases: [null],
|
|
110
|
+
outputTool: NOTARY_OUTPUT_TOOL_NAME,
|
|
111
|
+
inputFlag: "ak-notary-source-run",
|
|
112
|
+
phaseFlag: undefined,
|
|
113
|
+
activationStage: "load-and-install",
|
|
114
|
+
sessionMaterials: NOTARY_SESSION_MATERIALS,
|
|
115
|
+
},
|
|
20
116
|
] as const;
|
|
21
117
|
|
|
22
|
-
export type
|
|
23
|
-
export type
|
|
118
|
+
export type PublicRoleRecord = (typeof PUBLIC_ROLE_RECORDS)[number];
|
|
119
|
+
export type PackagedRole = PublicRoleRecord["role"];
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Read-only metadata projection (no sessionMaterials).
|
|
123
|
+
* Distributed per PublicRoleRecord member so role↔field associations stay intact.
|
|
124
|
+
*/
|
|
125
|
+
export type PackagedRoleMetadata = PublicRoleRecord extends infer R
|
|
126
|
+
? R extends PublicRoleRecord
|
|
127
|
+
? Omit<R, "sessionMaterials">
|
|
128
|
+
: never
|
|
129
|
+
: never;
|
|
130
|
+
|
|
131
|
+
/** Historical symbol — derived from PUBLIC_ROLE_RECORDS. */
|
|
132
|
+
export const PACKAGED_ROLE_REGISTRY: readonly PackagedRoleMetadata[] =
|
|
133
|
+
PUBLIC_ROLE_RECORDS.map(({ sessionMaterials: _omit, ...metadata }) => metadata);
|
|
24
134
|
|
|
25
135
|
export function packagedRoleMetadata(role: string): PackagedRoleMetadata | undefined {
|
|
26
136
|
return PACKAGED_ROLE_REGISTRY.find((entry) => entry.role === role);
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseSkillBlock,
|
|
3
|
+
type ExtensionAPI,
|
|
4
|
+
type ExtensionContext,
|
|
5
|
+
type SessionManager,
|
|
6
|
+
type ToolDefinition,
|
|
7
|
+
} from "@earendil-works/pi-coding-agent";
|
|
8
|
+
import type { Static, TSchema } from "typebox";
|
|
9
|
+
import { requireGatekeeperPass, type GatekeeperNonPassResult, type GatekeeperSubject } from "../gatekeeper-role.ts";
|
|
10
|
+
import type {
|
|
11
|
+
HostContext,
|
|
12
|
+
HostEventRegistration,
|
|
13
|
+
HostGatekeeperActions,
|
|
14
|
+
HostToolDefinition,
|
|
15
|
+
HostToolResult,
|
|
16
|
+
RoleHost,
|
|
17
|
+
} from "../host-contracts.ts";
|
|
18
|
+
|
|
19
|
+
export type PiRoleHostAdapter = {
|
|
20
|
+
readonly host: RoleHost;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const piContexts = new WeakMap<HostContext, ExtensionContext>();
|
|
24
|
+
|
|
25
|
+
/** Project Pi's activation context onto the package-owned host contract. */
|
|
26
|
+
function projectPiContext(context: ExtensionContext, transcriptFromContext?: (context: ExtensionContext) => string): HostContext {
|
|
27
|
+
const sessionManager = context.sessionManager as SessionManager;
|
|
28
|
+
const host: HostContext = {
|
|
29
|
+
cwd: context.cwd,
|
|
30
|
+
mode: context.mode,
|
|
31
|
+
model: context.model === undefined ? undefined : { provider: context.model.provider },
|
|
32
|
+
sessionManager: {
|
|
33
|
+
getLeafEntry: () => context.sessionManager.getLeafEntry(),
|
|
34
|
+
getLeafId: () => context.sessionManager.getLeafId(),
|
|
35
|
+
getEntries: () => context.sessionManager.getEntries(),
|
|
36
|
+
getSessionDir: () => context.sessionManager.getSessionDir(),
|
|
37
|
+
getSessionFile: () => context.sessionManager.getSessionFile(),
|
|
38
|
+
getHeader: () => context.sessionManager.getHeader(),
|
|
39
|
+
setSessionFile: (path) => sessionManager.setSessionFile(path),
|
|
40
|
+
appendCustomEntry: (customType, data) => sessionManager.appendCustomEntry(customType, data),
|
|
41
|
+
},
|
|
42
|
+
...(context.signal === undefined ? {} : { signal: context.signal }),
|
|
43
|
+
...(context.ui === undefined ? {} : { ui: { notify: (message, type) => context.ui.notify(message, type) } }),
|
|
44
|
+
...(transcriptFromContext === undefined ? {} : { transcript: () => transcriptFromContext(context) }),
|
|
45
|
+
abort: () => context.abort(),
|
|
46
|
+
};
|
|
47
|
+
piContexts.set(host, context);
|
|
48
|
+
return host;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Recover the Pi context only at the Pi composition boundary. */
|
|
52
|
+
export function toPiContext(context: HostContext): ExtensionContext {
|
|
53
|
+
const piContext = piContexts.get(context);
|
|
54
|
+
if (piContext === undefined) throw new Error("Host context is not backed by the Pi adapter");
|
|
55
|
+
return piContext;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Gatekeeper receives HostContext directly (#518 §1③) without recovering raw ExtensionContext via WeakMap. */
|
|
59
|
+
function requirePiGatekeeperPass(options: {
|
|
60
|
+
context: HostContext;
|
|
61
|
+
subject: GatekeeperSubject;
|
|
62
|
+
signal?: AbortSignal;
|
|
63
|
+
hostActions: HostGatekeeperActions;
|
|
64
|
+
toolCallId: string;
|
|
65
|
+
}): Promise<void> {
|
|
66
|
+
return requireGatekeeperPass({
|
|
67
|
+
context: options.context,
|
|
68
|
+
subject: options.subject,
|
|
69
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
70
|
+
hostActions: {
|
|
71
|
+
failInfrastructure(error, _context, toolCallId) {
|
|
72
|
+
options.hostActions.failInfrastructure(error, options.context, toolCallId);
|
|
73
|
+
},
|
|
74
|
+
bindSubmissionNonPass: options.hostActions.bindSubmissionNonPass,
|
|
75
|
+
},
|
|
76
|
+
toolCallId: options.toolCallId,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function toPiResult<D>(result: HostToolResult<D>): HostToolResult<D> {
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function toPiToolDefinition<S extends TSchema, D>(
|
|
85
|
+
tool: HostToolDefinition<S, D>,
|
|
86
|
+
projectContext: (context: ExtensionContext) => HostContext,
|
|
87
|
+
): ToolDefinition<S, D> {
|
|
88
|
+
return {
|
|
89
|
+
name: tool.name,
|
|
90
|
+
label: tool.label,
|
|
91
|
+
description: tool.description,
|
|
92
|
+
...(tool.promptSnippet === undefined ? {} : { promptSnippet: tool.promptSnippet }),
|
|
93
|
+
parameters: tool.parameters,
|
|
94
|
+
execute: async (toolCallId, params, signal, update, context) =>
|
|
95
|
+
toPiResult(await tool.execute(
|
|
96
|
+
toolCallId,
|
|
97
|
+
params as Static<S>,
|
|
98
|
+
signal,
|
|
99
|
+
update === undefined ? undefined : (result) => update(toPiResult(result)),
|
|
100
|
+
projectContext(context),
|
|
101
|
+
)),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Pi composition boundary. Each consumed capability is adapted explicitly. */
|
|
106
|
+
export function createPiRoleHostAdapter(
|
|
107
|
+
pi: ExtensionAPI,
|
|
108
|
+
options: { transcriptFromContext?: (context: ExtensionContext) => string } = {},
|
|
109
|
+
): PiRoleHostAdapter {
|
|
110
|
+
const host: RoleHost = {
|
|
111
|
+
capabilities: {
|
|
112
|
+
skillExpansion(prompt) {
|
|
113
|
+
const parsed = parseSkillBlock(prompt);
|
|
114
|
+
if (parsed == null) return undefined;
|
|
115
|
+
const userMessage = parsed.userMessage ?? "";
|
|
116
|
+
return Object.freeze({
|
|
117
|
+
name: parsed.name,
|
|
118
|
+
location: parsed.location,
|
|
119
|
+
content: parsed.content,
|
|
120
|
+
userMessage,
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
registerFlag: (name, definition) => pi.registerFlag(name, definition),
|
|
125
|
+
getFlag: (name) => pi.getFlag(name),
|
|
126
|
+
registerTool: (tool) => pi.registerTool(toPiToolDefinition(
|
|
127
|
+
tool,
|
|
128
|
+
(context) => projectPiContext(context, options.transcriptFromContext),
|
|
129
|
+
)),
|
|
130
|
+
getAllTools: () => pi.getAllTools().map(({ name, sourceInfo }) => ({
|
|
131
|
+
name,
|
|
132
|
+
...(sourceInfo?.path === undefined ? {} : { sourceInfo: { path: sourceInfo.path } }),
|
|
133
|
+
})),
|
|
134
|
+
setActiveTools: (names) => pi.setActiveTools(names),
|
|
135
|
+
getActiveTools: () => pi.getActiveTools(),
|
|
136
|
+
requireGatekeeperPass: requirePiGatekeeperPass,
|
|
137
|
+
on(...registration: HostEventRegistration) {
|
|
138
|
+
const context = (value: ExtensionContext) => projectPiContext(value, options.transcriptFromContext);
|
|
139
|
+
if (registration[0] === "before_agent_start") {
|
|
140
|
+
const [, handler] = registration;
|
|
141
|
+
pi.on("before_agent_start", (value, ctx) => handler({ prompt: value.prompt, systemPrompt: value.systemPrompt, systemPromptOptions: value.systemPromptOptions }, context(ctx)));
|
|
142
|
+
} else if (registration[0] === "input") {
|
|
143
|
+
const [, handler] = registration;
|
|
144
|
+
pi.on("input", (value, ctx) => handler({ text: value.text, ...(value.images === undefined ? {} : { images: value.images }), source: value.source }, context(ctx)));
|
|
145
|
+
} else if (registration[0] === "tool_call") {
|
|
146
|
+
const [, handler] = registration;
|
|
147
|
+
pi.on("tool_call", (value, ctx) => handler({ toolName: value.toolName, toolCallId: value.toolCallId, input: value.input }, context(ctx)));
|
|
148
|
+
} else if (registration[0] === "tool_result") {
|
|
149
|
+
const [, handler] = registration;
|
|
150
|
+
pi.on("tool_result", (value, ctx) => handler({ toolName: value.toolName, toolCallId: value.toolCallId, isError: value.isError, content: (value.content ?? []).map((part) => part.type === "text" ? { type: "text", text: part.text } : { type: "image", data: part.data, mimeType: part.mimeType }), details: value.details }, context(ctx)));
|
|
151
|
+
} else if (registration[0] === "session_start") {
|
|
152
|
+
const [, handler] = registration;
|
|
153
|
+
pi.on("session_start", (value, ctx) => handler({ reason: value.reason }, context(ctx)));
|
|
154
|
+
} else if (registration[0] === "session_shutdown") {
|
|
155
|
+
const [, handler] = registration;
|
|
156
|
+
pi.on("session_shutdown", (_value, ctx) => handler({}, context(ctx)));
|
|
157
|
+
} else if (registration[0] === "after_provider_response") {
|
|
158
|
+
const [, handler] = registration;
|
|
159
|
+
pi.on("after_provider_response", (value, ctx) => handler({ status: value.status }, context(ctx)));
|
|
160
|
+
} else if (registration[0] === "agent_end") {
|
|
161
|
+
const [, handler] = registration;
|
|
162
|
+
pi.on("agent_end", (value, ctx) => handler({
|
|
163
|
+
messages: value.messages.map((message) => ({
|
|
164
|
+
role: message.role,
|
|
165
|
+
content: "content" in message && Array.isArray(message.content) ? message.content.map((part) => part.type === "text"
|
|
166
|
+
? { type: "text", text: part.text }
|
|
167
|
+
: part.type === "toolCall"
|
|
168
|
+
? { type: "toolCall", id: part.id, name: part.name, arguments: part.arguments }
|
|
169
|
+
: { type: part.type }) : [],
|
|
170
|
+
...("toolName" in message && typeof message.toolName === "string" ? { toolName: message.toolName } : {}),
|
|
171
|
+
...("isError" in message && typeof message.isError === "boolean" ? { isError: message.isError } : {}),
|
|
172
|
+
...("stopReason" in message && typeof message.stopReason === "string" ? { stopReason: message.stopReason } : {}),
|
|
173
|
+
})),
|
|
174
|
+
}, context(ctx)));
|
|
175
|
+
} else if (registration[0] === "agent_settled") {
|
|
176
|
+
const [, handler] = registration;
|
|
177
|
+
pi.on("agent_settled", (_value, ctx) => handler({}, context(ctx)));
|
|
178
|
+
} else if (registration[0] === "tool_execution_start") {
|
|
179
|
+
const [, handler] = registration;
|
|
180
|
+
pi.on("tool_execution_start", (value, ctx) => handler({ toolName: value.toolName, toolCallId: value.toolCallId }, context(ctx)));
|
|
181
|
+
} else if (registration[0] === "tool_execution_update") {
|
|
182
|
+
const [, handler] = registration;
|
|
183
|
+
pi.on("tool_execution_update", (value, ctx) => handler({ toolName: value.toolName, toolCallId: value.toolCallId, partialResult: value.partialResult }, context(ctx)));
|
|
184
|
+
} else {
|
|
185
|
+
const [, handler] = registration;
|
|
186
|
+
pi.on("tool_execution_end", (value, ctx) => handler({ toolName: value.toolName, toolCallId: value.toolCallId, isError: value.isError }, context(ctx)));
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
...(typeof pi.getCommands === "function" ? { getCommands: () => pi.getCommands().map(({ name }) => ({ name })) } : {}),
|
|
190
|
+
};
|
|
191
|
+
return { host };
|
|
192
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { lstat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
DurablePrincipal,
|
|
6
|
+
DurablePrincipalAuthority,
|
|
7
|
+
DurablePrincipalCoordinates,
|
|
8
|
+
NewDurablePrincipalRequest,
|
|
9
|
+
} from "../host-contracts.ts";
|
|
10
|
+
import { resolveBookKeyFromGit } from "../activation-ledger-git.ts";
|
|
11
|
+
import {
|
|
12
|
+
activationBookDirectory,
|
|
13
|
+
resolveActivationLedgerHome,
|
|
14
|
+
} from "../activation-ledger-topology.ts";
|
|
15
|
+
|
|
16
|
+
type PiDurablePrincipal = DurablePrincipal & {
|
|
17
|
+
readonly sessionDirectory: string;
|
|
18
|
+
readonly sessionFile: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function encode(coordinates: DurablePrincipalCoordinates): PiDurablePrincipal {
|
|
22
|
+
return coordinates as PiDurablePrincipal;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function issuePiDurablePrincipalCoordinates(
|
|
26
|
+
request: NewDurablePrincipalRequest,
|
|
27
|
+
): DurablePrincipalCoordinates & {
|
|
28
|
+
readonly ledgerHome: string;
|
|
29
|
+
readonly bookKey: string;
|
|
30
|
+
readonly runDirectory: string;
|
|
31
|
+
} {
|
|
32
|
+
const ledgerHome = resolveActivationLedgerHome(
|
|
33
|
+
request.home === undefined ? undefined : () => request.home!,
|
|
34
|
+
);
|
|
35
|
+
const bookKey = resolveBookKeyFromGit(request.cwd);
|
|
36
|
+
const runDirectory = join(
|
|
37
|
+
activationBookDirectory(ledgerHome, bookKey),
|
|
38
|
+
"runs",
|
|
39
|
+
`${request.runId}@${request.role}`,
|
|
40
|
+
);
|
|
41
|
+
const sessionDirectory = join(runDirectory, "session");
|
|
42
|
+
return {
|
|
43
|
+
ledgerHome,
|
|
44
|
+
bookKey,
|
|
45
|
+
runDirectory,
|
|
46
|
+
sessionDirectory,
|
|
47
|
+
sessionFile: join(sessionDirectory, "session.jsonl"),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Pi's durable-principal codec and availability authority. */
|
|
52
|
+
export const piDurablePrincipalAuthority: DurablePrincipalAuthority = {
|
|
53
|
+
issue(request: NewDurablePrincipalRequest): DurablePrincipal {
|
|
54
|
+
const coordinates = issuePiDurablePrincipalCoordinates(request);
|
|
55
|
+
return encode({
|
|
56
|
+
sessionDirectory: coordinates.sessionDirectory,
|
|
57
|
+
sessionFile: coordinates.sessionFile,
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
decode(value: unknown): DurablePrincipalCoordinates {
|
|
61
|
+
const record = value as Partial<DurablePrincipalCoordinates> | null;
|
|
62
|
+
if (record === null || typeof record !== "object") {
|
|
63
|
+
throw new Error("durable principal payload is missing");
|
|
64
|
+
}
|
|
65
|
+
if (typeof record.sessionDirectory !== "string" || record.sessionDirectory.trim() === "") {
|
|
66
|
+
throw new Error("durable principal session directory is missing");
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
sessionDirectory: record.sessionDirectory,
|
|
70
|
+
sessionFile:
|
|
71
|
+
typeof record.sessionFile === "string" && record.sessionFile.trim() !== ""
|
|
72
|
+
? record.sessionFile
|
|
73
|
+
: join(record.sessionDirectory, "session.jsonl"),
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
async isAvailable(principal: DurablePrincipal): Promise<boolean> {
|
|
77
|
+
const { sessionFile } = this.decode(principal);
|
|
78
|
+
try {
|
|
79
|
+
const stat = await lstat(sessionFile);
|
|
80
|
+
return stat.isFile() && !stat.isSymbolicLink();
|
|
81
|
+
} catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Transitional coordinate accessor.
|
|
89
|
+
* S1b-2 (#526) removed 13 per-role builder accessors and auto-resume decode
|
|
90
|
+
* consumption. Remaining consumers: admission persistence projection and
|
|
91
|
+
* settlement/inspect (S4 #519 retires this seam).
|
|
92
|
+
*/
|
|
93
|
+
export function decodePiDurablePrincipal(
|
|
94
|
+
authority: DurablePrincipalAuthority,
|
|
95
|
+
principal: DurablePrincipal,
|
|
96
|
+
): DurablePrincipalCoordinates {
|
|
97
|
+
return authority.decode(principal);
|
|
98
|
+
}
|