@nt-ai-lab/deterministic-agent-workflow-pi 0.5.5-issue-526.ce3247e.0 → 0.5.6
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/dist/features/pi-extension/entrypoint/pi-workflow-automation.d.ts +22 -0
- package/dist/features/pi-extension/entrypoint/pi-workflow-automation.js +160 -0
- package/dist/features/pi-extension/entrypoint/pi-workflow-extension-platform.d.ts +1 -1
- package/dist/features/pi-extension/entrypoint/pi-workflow-extension.d.ts +1 -1
- package/dist/features/pi-extension/entrypoint/pi-workflow-extension.js +16 -2
- package/dist/index.d.ts +5 -5
- package/dist/platform/domain/pi-workflow-extension-types.d.ts +16 -2
- package/dist/platform/infra/external-clients/pi/pi-extension-context-window.d.ts +3 -0
- package/dist/platform/infra/external-clients/pi/pi-extension-context-window.js +77 -0
- package/package.json +4 -4
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import type { PiWorkflowIdleContext } from '../../../platform/domain/pi-workflow-extension-types.js';
|
|
3
|
+
/** @riviere-role cli-entrypoint */
|
|
4
|
+
export declare function registerPiWorkflowAutomation<TState extends {
|
|
5
|
+
readonly currentStateMachineState: string;
|
|
6
|
+
}>(pi: ExtensionAPI, options: {
|
|
7
|
+
readonly databasePath: string;
|
|
8
|
+
readonly automation?: {
|
|
9
|
+
readonly ownsState: (state: TState) => boolean;
|
|
10
|
+
readonly onIdle: (context: PiWorkflowIdleContext<TState>) => Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
readonly isReady: (ctx: ExtensionContext) => boolean;
|
|
13
|
+
readonly getState: (ctx: ExtensionContext) => TState;
|
|
14
|
+
readonly runOperation: (ctx: ExtensionContext, args: readonly string[]) => {
|
|
15
|
+
readonly exitCode: number;
|
|
16
|
+
readonly output: string;
|
|
17
|
+
};
|
|
18
|
+
readonly fail: (ctx: ExtensionContext, reason: string) => void;
|
|
19
|
+
}): {
|
|
20
|
+
ownsState: (ctx: ExtensionContext) => boolean;
|
|
21
|
+
afterOperation: (ctx: ExtensionContext) => void;
|
|
22
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { ReviewCoordinator } from '@nt-ai-lab/deterministic-agent-workflow-cli';
|
|
2
|
+
import { WorkflowStateError } from '@nt-ai-lab/deterministic-agent-workflow-engine';
|
|
3
|
+
import { createStore } from '@nt-ai-lab/deterministic-agent-workflow-event-store';
|
|
4
|
+
import { createPiExtensionContextWindow } from '../../../platform/infra/external-clients/pi/pi-extension-context-window.js';
|
|
5
|
+
/** @riviere-role cli-entrypoint */
|
|
6
|
+
export function registerPiWorkflowAutomation(pi, options) {
|
|
7
|
+
const automation = options.automation;
|
|
8
|
+
if (automation === undefined)
|
|
9
|
+
return {
|
|
10
|
+
ownsState: () => false,
|
|
11
|
+
afterOperation: () => undefined,
|
|
12
|
+
};
|
|
13
|
+
const active = new Map();
|
|
14
|
+
const cancellations = new Map();
|
|
15
|
+
const refreshContext = createPiExtensionContextWindow(pi);
|
|
16
|
+
const fail = (ctx, error) => {
|
|
17
|
+
options.fail(ctx, `Workflow automation failed: ${String(error)}`);
|
|
18
|
+
};
|
|
19
|
+
const isReady = (ctx) => {
|
|
20
|
+
try {
|
|
21
|
+
return options.isReady(ctx);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
fail(ctx, error);
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const ownsState = (ctx) => {
|
|
29
|
+
if (!isReady(ctx))
|
|
30
|
+
return false;
|
|
31
|
+
try {
|
|
32
|
+
return active.has(ctx.sessionManager.getSessionId()) || automation.ownsState(options.getState(ctx));
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
fail(ctx, error);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const runIdle = async (ctx) => {
|
|
40
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
41
|
+
if (!isReady(ctx) || !ctx.isIdle() || active.has(sessionId) || !ownsState(ctx))
|
|
42
|
+
return;
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
active.set(sessionId, controller);
|
|
45
|
+
const stops = new Set();
|
|
46
|
+
cancellations.set(sessionId, stops);
|
|
47
|
+
const requireActive = () => {
|
|
48
|
+
if (controller.signal.aborted || active.get(sessionId) !== controller ||
|
|
49
|
+
ctx.sessionManager.getSessionId() !== sessionId || !isReady(ctx)) {
|
|
50
|
+
throw new WorkflowStateError('The workflow automation context is no longer active.');
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const resume = {};
|
|
54
|
+
try {
|
|
55
|
+
await automation.onIdle({
|
|
56
|
+
sessionId,
|
|
57
|
+
signal: controller.signal,
|
|
58
|
+
workingDirectory: ctx.cwd,
|
|
59
|
+
getState: () => {
|
|
60
|
+
requireActive();
|
|
61
|
+
return options.getState(ctx);
|
|
62
|
+
},
|
|
63
|
+
runOperation: (operation, ...args) => {
|
|
64
|
+
requireActive();
|
|
65
|
+
const result = options.runOperation(ctx, [operation, ...args]);
|
|
66
|
+
if (result.exitCode !== 0)
|
|
67
|
+
throw new WorkflowStateError(result.output);
|
|
68
|
+
return result.output;
|
|
69
|
+
},
|
|
70
|
+
runReviews: async (request, client) => {
|
|
71
|
+
requireActive();
|
|
72
|
+
const store = createStore(options.databasePath);
|
|
73
|
+
const coordinator = new ReviewCoordinator({
|
|
74
|
+
store,
|
|
75
|
+
client,
|
|
76
|
+
now: () => new Date().toISOString()
|
|
77
|
+
});
|
|
78
|
+
const cancel = async () => {
|
|
79
|
+
const result = await coordinator.cancel(request.bundleId, 'Pi session shutdown');
|
|
80
|
+
if (result.type === 'failed')
|
|
81
|
+
throw new WorkflowStateError(result.reason);
|
|
82
|
+
};
|
|
83
|
+
stops.add(cancel);
|
|
84
|
+
try {
|
|
85
|
+
return await coordinator.run({
|
|
86
|
+
...request,
|
|
87
|
+
sessionId,
|
|
88
|
+
workingDirectory: ctx.cwd
|
|
89
|
+
}, options.getState(ctx).currentStateMachineState);
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
stops.delete(cancel);
|
|
93
|
+
store.db.close();
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
resumeWithFreshContext: (instructions) => {
|
|
97
|
+
requireActive();
|
|
98
|
+
if (resume.instructions !== undefined)
|
|
99
|
+
throw new WorkflowStateError('Workflow automation already requested a fresh context.');
|
|
100
|
+
if (automation.ownsState(options.getState(ctx))) {
|
|
101
|
+
throw new WorkflowStateError('Cannot resume the conversational agent in a workflow-owned state.');
|
|
102
|
+
}
|
|
103
|
+
refreshContext(ctx, instructions);
|
|
104
|
+
resume.instructions = instructions;
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
fail(ctx, error);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
active.delete(sessionId);
|
|
114
|
+
cancellations.delete(sessionId);
|
|
115
|
+
}
|
|
116
|
+
if (!controller.signal.aborted && resume.instructions !== undefined) {
|
|
117
|
+
pi.sendUserMessage(resume.instructions);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
pi.on('session_shutdown', async (_event, ctx) => {
|
|
121
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
122
|
+
active.get(sessionId)?.abort();
|
|
123
|
+
const results = await Promise.allSettled([...cancellations.get(sessionId) ?? []].map((cancel) => cancel()));
|
|
124
|
+
for (const result of results) {
|
|
125
|
+
if (result.status === 'rejected')
|
|
126
|
+
fail(ctx, result.reason);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
pi.on('agent_settled', (_event, ctx) => runIdle(ctx));
|
|
130
|
+
pi.on('session_start', (_event, ctx) => runIdle(ctx));
|
|
131
|
+
pi.on('tool_call', (_event, ctx) => {
|
|
132
|
+
if (ownsState(ctx))
|
|
133
|
+
return {
|
|
134
|
+
block: true,
|
|
135
|
+
reason: 'The workflow owns this state; conversational tools are disabled.'
|
|
136
|
+
};
|
|
137
|
+
return undefined;
|
|
138
|
+
});
|
|
139
|
+
pi.on('input', (_event, ctx) => {
|
|
140
|
+
if (ownsState(ctx))
|
|
141
|
+
return { action: 'handled' };
|
|
142
|
+
return undefined;
|
|
143
|
+
});
|
|
144
|
+
pi.on('session_before_switch', (_event, ctx) => {
|
|
145
|
+
if (active.has(ctx.sessionManager.getSessionId()))
|
|
146
|
+
return { cancel: true };
|
|
147
|
+
return undefined;
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
ownsState,
|
|
151
|
+
afterOperation: (ctx) => {
|
|
152
|
+
if (!ownsState(ctx))
|
|
153
|
+
return;
|
|
154
|
+
if (ctx.isIdle())
|
|
155
|
+
void runIdle(ctx);
|
|
156
|
+
else
|
|
157
|
+
ctx.abort();
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';
|
|
2
2
|
import type { RunnerResult } from '@nt-ai-lab/deterministic-agent-workflow-cli';
|
|
3
|
-
import type { PiSessionIdResult } from '../../../platform/domain/pi-workflow-extension-types';
|
|
3
|
+
import type { PiSessionIdResult } from '../../../platform/domain/pi-workflow-extension-types.js';
|
|
4
4
|
/** @riviere-role cli-entrypoint */
|
|
5
5
|
export declare function translationNote(toolName: string): string;
|
|
6
6
|
/** @riviere-role cli-entrypoint */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BaseWorkflowState, RehydratableWorkflow, TransitionContext } from '@nt-ai-lab/deterministic-agent-workflow-engine';
|
|
2
|
-
import type { PiWorkflowExtension, PiWorkflowExtensionConfig } from '../../../platform/domain/pi-workflow-extension-types';
|
|
2
|
+
import type { PiWorkflowExtension, PiWorkflowExtensionConfig } from '../../../platform/domain/pi-workflow-extension-types.js';
|
|
3
3
|
export declare const PI_IDLE_RECOVERY_MESSAGE: string;
|
|
4
4
|
export declare const PI_SESSION_BRANCH_BLOCK_MESSAGE = "Pi session tree navigation and forks are disabled while a workflow is active.";
|
|
5
5
|
/** @riviere-role cli-entrypoint */
|
|
@@ -8,6 +8,7 @@ import { hasPiWorkflowMarker, PI_WORKFLOW_MARKER_CUSTOM_TYPE, } from '../../../p
|
|
|
8
8
|
import { getLatestPiAssistantSettlement, PiTranscriptReader, } from '../../../platform/infra/external-clients/pi/pi-transcript-reader.js';
|
|
9
9
|
import { notifyRouteResult, readSessionId, readWorkflowInstruction, requireSessionFile, resolveDatabasePath, translationNote, } from './pi-workflow-extension-platform.js';
|
|
10
10
|
import { createPiWorkflowSessionOwnership } from './pi-workflow-session-ownership.js';
|
|
11
|
+
import { registerPiWorkflowAutomation } from './pi-workflow-automation.js';
|
|
11
12
|
const PI_QUESTION_TOOL = 'question';
|
|
12
13
|
const DEFAULT_COMMAND_NAME = 'workflow';
|
|
13
14
|
const DEFAULT_TOOL_NAME = 'workflow';
|
|
@@ -291,7 +292,7 @@ export function createPiWorkflowExtension(config) {
|
|
|
291
292
|
return { action: 'handled' };
|
|
292
293
|
});
|
|
293
294
|
pi.on('agent_settled', (_event, ctx) => {
|
|
294
|
-
if (isInactive(ctx) || readinessFailure(ctx) !== undefined)
|
|
295
|
+
if (isInactive(ctx) || readinessFailure(ctx) !== undefined || automation.ownsState(ctx))
|
|
295
296
|
return;
|
|
296
297
|
const session = readSessionId(ctx);
|
|
297
298
|
if (!session.ok)
|
|
@@ -340,6 +341,8 @@ export function createPiWorkflowExtension(config) {
|
|
|
340
341
|
executionMode: 'sequential',
|
|
341
342
|
async execute(_toolCallId, parameters, _signal, _onUpdate, ctx) {
|
|
342
343
|
const result = runRoute(ctx, [parameters.operation, ...(parameters.args ?? [])], pi);
|
|
344
|
+
if (result.exitCode === 0)
|
|
345
|
+
automation.afterOperation(ctx);
|
|
343
346
|
return {
|
|
344
347
|
content: [{
|
|
345
348
|
type: 'text',
|
|
@@ -355,12 +358,23 @@ export function createPiWorkflowExtension(config) {
|
|
|
355
358
|
handler: async (rawArguments, ctx) => {
|
|
356
359
|
try {
|
|
357
360
|
const result = runRoute(ctx, parsePiCommandArguments(rawArguments), pi);
|
|
358
|
-
|
|
361
|
+
if (result.exitCode === 0)
|
|
362
|
+
automation.afterOperation(ctx);
|
|
363
|
+
if (result.exitCode !== 0 || (!automation.ownsState(ctx) && readinessFailure(ctx) === undefined))
|
|
364
|
+
notifyRouteResult(ctx, pi, result);
|
|
359
365
|
}
|
|
360
366
|
catch (error) {
|
|
361
367
|
ctx.ui.notify(String(error), 'error');
|
|
362
368
|
}
|
|
363
369
|
},
|
|
364
370
|
});
|
|
371
|
+
const automation = registerPiWorkflowAutomation(pi, {
|
|
372
|
+
databasePath,
|
|
373
|
+
...(config.automation === undefined ? {} : { automation: config.automation }),
|
|
374
|
+
isReady: (ctx) => readinessFailure(ctx) === undefined && !ownership.usesInheritedWorkflow(ctx.sessionManager.getSessionId()),
|
|
375
|
+
getState: (ctx) => useEngine(ctx, (engine) => engine.getWorkflowState(ctx.sessionManager.getSessionId())),
|
|
376
|
+
runOperation: (ctx, args) => runRoute(ctx, args, pi),
|
|
377
|
+
fail: (ctx, reason) => markSafetyUnavailable(ctx, ctx.sessionManager.getSessionId(), reason),
|
|
378
|
+
});
|
|
365
379
|
};
|
|
366
380
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { createPiWorkflowExtension, PI_IDLE_RECOVERY_MESSAGE, PI_SESSION_BRANCH_BLOCK_MESSAGE, } from './features/pi-extension/entrypoint/pi-workflow-extension';
|
|
2
|
-
export type { PiWorkflowExtension, PiWorkflowExtensionConfig, } from './platform/domain/pi-workflow-extension-types';
|
|
3
|
-
export { resolvePiMainSessionId } from './platform/domain/pi-main-session';
|
|
4
|
-
export { refreshPiContextWindow } from './platform/infra/external-clients/pi/pi-context-window';
|
|
5
|
-
export { PiTranscriptReader } from './platform/infra/external-clients/pi/pi-transcript-reader';
|
|
1
|
+
export { createPiWorkflowExtension, PI_IDLE_RECOVERY_MESSAGE, PI_SESSION_BRANCH_BLOCK_MESSAGE, } from './features/pi-extension/entrypoint/pi-workflow-extension.js';
|
|
2
|
+
export type { PiWorkflowExtension, PiWorkflowExtensionConfig, PiWorkflowIdleContext, } from './platform/domain/pi-workflow-extension-types.js';
|
|
3
|
+
export { resolvePiMainSessionId } from './platform/domain/pi-main-session.js';
|
|
4
|
+
export { refreshPiContextWindow } from './platform/infra/external-clients/pi/pi-context-window.js';
|
|
5
|
+
export { PiTranscriptReader } from './platform/infra/external-clients/pi/pi-transcript-reader.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ExtensionFactory } from '@earendil-works/pi-coding-agent';
|
|
2
|
-
import type { BaseWorkflowState, RehydratableWorkflow, TransitionContext, WorkflowDefinition } from '@nt-ai-lab/deterministic-agent-workflow-engine';
|
|
3
|
-
import type { PlatformContext, PreToolUseHandlerConfig, RouteMap } from '@nt-ai-lab/deterministic-agent-workflow-cli';
|
|
2
|
+
import type { BaseWorkflowState, RehydratableWorkflow, ReviewBundleRequest, TransitionContext, WorkflowDefinition } from '@nt-ai-lab/deterministic-agent-workflow-engine';
|
|
3
|
+
import type { PlatformContext, PreToolUseHandlerConfig, RouteMap, ReviewAgentClient, ReviewCoordinatorResult } from '@nt-ai-lab/deterministic-agent-workflow-cli';
|
|
4
4
|
/** @riviere-role value-object */
|
|
5
5
|
export type PiWorkflowExtension = ExtensionFactory;
|
|
6
6
|
/** @riviere-role value-object */
|
|
@@ -23,6 +23,16 @@ export type PiSessionIdResult = {
|
|
|
23
23
|
readonly reason: string;
|
|
24
24
|
};
|
|
25
25
|
/** @riviere-role value-object */
|
|
26
|
+
export interface PiWorkflowIdleContext<TState> {
|
|
27
|
+
readonly sessionId: string;
|
|
28
|
+
readonly signal: AbortSignal;
|
|
29
|
+
readonly workingDirectory: string;
|
|
30
|
+
getState(): TState;
|
|
31
|
+
runOperation(operation: string, ...args: readonly string[]): string;
|
|
32
|
+
runReviews(request: Omit<ReviewBundleRequest, 'sessionId' | 'workingDirectory'>, client: ReviewAgentClient): Promise<ReviewCoordinatorResult>;
|
|
33
|
+
resumeWithFreshContext(stateInstructions: string): void;
|
|
34
|
+
}
|
|
35
|
+
/** @riviere-role value-object */
|
|
26
36
|
export type PiWorkflowExtensionConfig<TWorkflow extends RehydratableWorkflow<TState>, TState extends BaseWorkflowState<TStateName>, TDeps, TStateName extends string = string, TOperation extends string = string, TTransitionContext extends TransitionContext<TState, TStateName> = TransitionContext<TState, TStateName>> = Omit<PreToolUseHandlerConfig<TWorkflow, TState, TStateName>, 'questionToolName'> & {
|
|
27
37
|
readonly workflowDefinition: WorkflowDefinition<TWorkflow, TState, TDeps, TStateName, TOperation, TTransitionContext>;
|
|
28
38
|
readonly routes: RouteMap<TWorkflow, TState>;
|
|
@@ -33,4 +43,8 @@ export type PiWorkflowExtensionConfig<TWorkflow extends RehydratableWorkflow<TSt
|
|
|
33
43
|
readonly commandName?: string;
|
|
34
44
|
readonly toolName?: string;
|
|
35
45
|
readonly stopPreventionMessage?: string;
|
|
46
|
+
readonly automation?: {
|
|
47
|
+
readonly ownsState: (state: TState) => boolean;
|
|
48
|
+
readonly onIdle: (context: PiWorkflowIdleContext<TState>) => Promise<void>;
|
|
49
|
+
};
|
|
36
50
|
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { buildSessionContext, } from '@earendil-works/pi-coding-agent';
|
|
3
|
+
const BOUNDARY_TYPE = 'pi-context-window-state';
|
|
4
|
+
function readBoundary(ctx) {
|
|
5
|
+
const branch = ctx.sessionManager.getBranch();
|
|
6
|
+
const boundary = branch.findLast((entry) => entry.type === 'custom' && entry.customType === BOUNDARY_TYPE);
|
|
7
|
+
if (boundary === undefined)
|
|
8
|
+
return undefined;
|
|
9
|
+
if (boundary.type !== 'custom' || typeof boundary.data !== 'string' || boundary.data.trim().length === 0) {
|
|
10
|
+
throw new TypeError('Pi context boundary has invalid state instructions.');
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
boundary,
|
|
14
|
+
instructions: boundary.data,
|
|
15
|
+
entries: branch.slice(branch.indexOf(boundary))
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** @riviere-role external-client-service */
|
|
19
|
+
export function createPiExtensionContextWindow(pi) {
|
|
20
|
+
pi.on('context', (_event, ctx) => {
|
|
21
|
+
try {
|
|
22
|
+
const window = readBoundary(ctx);
|
|
23
|
+
if (window === undefined)
|
|
24
|
+
return;
|
|
25
|
+
return {
|
|
26
|
+
messages: [{
|
|
27
|
+
role: 'user',
|
|
28
|
+
content: window.instructions,
|
|
29
|
+
timestamp: Date.parse(window.boundary.timestamp),
|
|
30
|
+
}, ...buildSessionContext(window.entries).messages],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
ctx.abort();
|
|
35
|
+
ctx.ui.notify(`Pi context safety is unavailable: ${String(error)}`, 'error');
|
|
36
|
+
ctx.shutdown();
|
|
37
|
+
return { messages: [] };
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
pi.on('session_before_compact', (event, ctx) => {
|
|
41
|
+
try {
|
|
42
|
+
const window = readBoundary(ctx);
|
|
43
|
+
if (window === undefined)
|
|
44
|
+
return;
|
|
45
|
+
return {
|
|
46
|
+
compaction: {
|
|
47
|
+
summary: window.instructions,
|
|
48
|
+
firstKeptEntryId: window.boundary.id,
|
|
49
|
+
tokensBefore: event.preparation.tokensBefore,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
ctx.abort();
|
|
55
|
+
ctx.ui.notify(`Pi context safety is unavailable: ${String(error)}`, 'error');
|
|
56
|
+
ctx.shutdown();
|
|
57
|
+
return { cancel: true };
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
return (ctx, instructions) => {
|
|
61
|
+
if (instructions.trim().length === 0) {
|
|
62
|
+
throw new TypeError('Pi context state instructions must not be empty.');
|
|
63
|
+
}
|
|
64
|
+
if (!ctx.isIdle() || ctx.hasPendingMessages()) {
|
|
65
|
+
throw new TypeError('Cannot refresh Pi context until the session is idle without pending messages.');
|
|
66
|
+
}
|
|
67
|
+
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
68
|
+
if (sessionFile === undefined || !existsSync(sessionFile)) {
|
|
69
|
+
throw new TypeError('Cannot refresh Pi context without a persisted session.');
|
|
70
|
+
}
|
|
71
|
+
pi.appendEntry(BOUNDARY_TYPE, instructions);
|
|
72
|
+
const window = readBoundary(ctx);
|
|
73
|
+
if (window?.instructions !== instructions || window.boundary.id !== ctx.sessionManager.getLeafId()) {
|
|
74
|
+
throw new TypeError('Pi did not persist the requested context boundary.');
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nt-ai-lab/deterministic-agent-workflow-pi",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"typebox": "^1.3.7",
|
|
26
|
-
"@nt-ai-lab/deterministic-agent-workflow-
|
|
27
|
-
"@nt-ai-lab/deterministic-agent-workflow-
|
|
28
|
-
"@nt-ai-lab/deterministic-agent-workflow-
|
|
26
|
+
"@nt-ai-lab/deterministic-agent-workflow-cli": "0.4.9",
|
|
27
|
+
"@nt-ai-lab/deterministic-agent-workflow-engine": "0.4.9",
|
|
28
|
+
"@nt-ai-lab/deterministic-agent-workflow-event-store": "0.4.9"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@earendil-works/pi-coding-agent": "^0.84.4",
|