@nt-ai-lab/deterministic-agent-workflow-pi 0.5.3 → 0.5.5-issue-526.ce3247e.0
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-extension.d.ts +1 -1
- package/dist/features/pi-extension/entrypoint/pi-workflow-extension.js +23 -35
- package/dist/features/pi-extension/entrypoint/pi-workflow-session-ownership.d.ts +12 -0
- package/dist/features/pi-extension/entrypoint/pi-workflow-session-ownership.js +72 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/platform/domain/pi-main-session.d.ts +2 -0
- package/dist/platform/domain/pi-main-session.js +15 -0
- package/dist/platform/domain/pi-workflow-extension-types.d.ts +2 -0
- package/dist/platform/infra/external-clients/pi/pi-context-window.d.ts +3 -0
- package/dist/platform/infra/external-clients/pi/pi-context-window.js +17 -0
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BaseWorkflowState, RehydratableWorkflow, TransitionContext } from '@nt-ai-lab/deterministic-agent-workflow-engine';
|
|
2
2
|
import type { PiWorkflowExtension, PiWorkflowExtensionConfig } from '../../../platform/domain/pi-workflow-extension-types';
|
|
3
|
-
export declare const PI_IDLE_RECOVERY_MESSAGE
|
|
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 */
|
|
6
6
|
export declare function createPiWorkflowExtension<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>>(config: PiWorkflowExtensionConfig<TWorkflow, TState, TDeps, TStateName, TOperation, TTransitionContext>): PiWorkflowExtension;
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { WorkflowEngine } from '@nt-ai-lab/deterministic-agent-workflow-engine';
|
|
3
|
-
import { createPreToolUseHandler, createWorkflowRunner, getRepositoryName, } from '@nt-ai-lab/deterministic-agent-workflow-cli';
|
|
3
|
+
import { createPreToolUseHandler, createWorkflowRunner, formatStopPreventionMessage, getRepositoryName, } from '@nt-ai-lab/deterministic-agent-workflow-cli';
|
|
4
4
|
import { createStore } from '@nt-ai-lab/deterministic-agent-workflow-event-store';
|
|
5
5
|
import { Type } from 'typebox';
|
|
6
6
|
import { parsePiCommandArguments } from '../../../platform/domain/pi-command-arguments.js';
|
|
7
|
-
import { hasPiWorkflowMarker, PI_WORKFLOW_MARKER_CUSTOM_TYPE,
|
|
7
|
+
import { hasPiWorkflowMarker, PI_WORKFLOW_MARKER_CUSTOM_TYPE, } from '../../../platform/infra/external-clients/pi/pi-session-file.js';
|
|
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
|
+
import { createPiWorkflowSessionOwnership } from './pi-workflow-session-ownership.js';
|
|
10
11
|
const PI_QUESTION_TOOL = 'question';
|
|
11
12
|
const DEFAULT_COMMAND_NAME = 'workflow';
|
|
12
13
|
const DEFAULT_TOOL_NAME = 'workflow';
|
|
13
14
|
const INITIALIZATION_PENDING_REASON = 'Pi workflow initialization has not completed safely. Tool execution is blocked.';
|
|
14
15
|
const INACTIVE_WORKFLOW_REASON = 'Pi workflow is inactive. Run the workflow init command before using workflow operations.';
|
|
15
|
-
export const PI_IDLE_RECOVERY_MESSAGE =
|
|
16
|
+
export const PI_IDLE_RECOVERY_MESSAGE = formatStopPreventionMessage();
|
|
16
17
|
export const PI_SESSION_BRANCH_BLOCK_MESSAGE = 'Pi session tree navigation and forks are disabled while a workflow is active.';
|
|
17
18
|
const workflowToolParameters = Type.Object({
|
|
18
19
|
operation: Type.String({ description: 'Workflow operation, for example init or transition' }),
|
|
@@ -23,6 +24,7 @@ export function createPiWorkflowExtension(config) {
|
|
|
23
24
|
const databasePath = resolveDatabasePath(config.databasePath);
|
|
24
25
|
const commandName = config.commandName ?? DEFAULT_COMMAND_NAME;
|
|
25
26
|
const toolName = config.toolName ?? DEFAULT_TOOL_NAME;
|
|
27
|
+
const ownership = createPiWorkflowSessionOwnership(databasePath);
|
|
26
28
|
const initializationBySession = new Map();
|
|
27
29
|
const sessionStartsById = new Map();
|
|
28
30
|
const recoveredAssistantBySession = new Map();
|
|
@@ -35,6 +37,7 @@ export function createPiWorkflowExtension(config) {
|
|
|
35
37
|
const runner = createWorkflowRunner({
|
|
36
38
|
workflowDefinition: config.workflowDefinition,
|
|
37
39
|
routes: config.routes,
|
|
40
|
+
unknownCommandMessage: config.unknownCommandMessage,
|
|
38
41
|
bashForbidden: config.bashForbidden,
|
|
39
42
|
isWriteAllowed: config.isWriteAllowed,
|
|
40
43
|
questionToolName: PI_QUESTION_TOOL,
|
|
@@ -53,7 +56,7 @@ export function createPiWorkflowExtension(config) {
|
|
|
53
56
|
appendToFile: () => undefined,
|
|
54
57
|
now,
|
|
55
58
|
transcriptReader: new PiTranscriptReader(() => ctx.sessionManager.getBranch()),
|
|
56
|
-
sessionContext: { getMainSessionId: () => sessionId },
|
|
59
|
+
sessionContext: { getMainSessionId: () => ownership.delegatedParent(sessionId, store) ?? sessionId, },
|
|
57
60
|
};
|
|
58
61
|
}
|
|
59
62
|
function useEngine(ctx, operation) {
|
|
@@ -62,6 +65,7 @@ export function createPiWorkflowExtension(config) {
|
|
|
62
65
|
const engineDeps = buildEngineDeps(ctx, store);
|
|
63
66
|
const now = engineDeps.now;
|
|
64
67
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
68
|
+
ownership.requireAccess(store, sessionId);
|
|
65
69
|
const platform = {
|
|
66
70
|
getPluginRoot: () => config.pluginRoot,
|
|
67
71
|
now,
|
|
@@ -76,15 +80,6 @@ export function createPiWorkflowExtension(config) {
|
|
|
76
80
|
store.db.close();
|
|
77
81
|
}
|
|
78
82
|
}
|
|
79
|
-
function hasPersistedWorkflowState(sessionId) {
|
|
80
|
-
const store = createStore(databasePath);
|
|
81
|
-
try {
|
|
82
|
-
return store.hasSessionStarted(sessionId);
|
|
83
|
-
}
|
|
84
|
-
finally {
|
|
85
|
-
store.db.close();
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
83
|
function markInitializationFailed(ctx, sessionId, detail) {
|
|
89
84
|
return failSession(ctx, sessionId, `Pi workflow initialization failed: ${detail}`);
|
|
90
85
|
}
|
|
@@ -113,23 +108,9 @@ export function createPiWorkflowExtension(config) {
|
|
|
113
108
|
}
|
|
114
109
|
function isInactive(ctx) {
|
|
115
110
|
const session = readSessionId(ctx);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const parentSessionFile = ctx.sessionManager.getHeader()?.parentSession;
|
|
120
|
-
if (parentSessionFile === undefined) {
|
|
121
|
-
return event.reason === 'fork' ? 'Forked Pi session has no verifiable parent session file.' : undefined;
|
|
122
|
-
}
|
|
123
|
-
const parent = readPiSessionMetadata(parentSessionFile);
|
|
124
|
-
const store = createStore(databasePath);
|
|
125
|
-
try {
|
|
126
|
-
return store.hasSessionStarted(parent.id) || parent.hasWorkflowMarker
|
|
127
|
-
? `Cannot fork Pi session ${parent.id}: its workflow is active.`
|
|
128
|
-
: undefined;
|
|
129
|
-
}
|
|
130
|
-
finally {
|
|
131
|
-
store.db.close();
|
|
132
|
-
}
|
|
111
|
+
if (!session.ok)
|
|
112
|
+
return false;
|
|
113
|
+
return initializationBySession.get(session.sessionId)?.type === 'inactive';
|
|
133
114
|
}
|
|
134
115
|
function initializeSession(event, ctx, pi, sessionId) {
|
|
135
116
|
try {
|
|
@@ -139,22 +120,29 @@ export function createPiWorkflowExtension(config) {
|
|
|
139
120
|
return 'Pi session header does not match the active session UUID.';
|
|
140
121
|
const activeBranchHasWorkflowMarker = hasPiWorkflowMarker(ctx.sessionManager.getBranch());
|
|
141
122
|
const sessionHasWorkflowMarker = hasPiWorkflowMarker(ctx.sessionManager.getEntries());
|
|
123
|
+
const inheritedWorkflowState = ownership.usesInheritedWorkflow(sessionId);
|
|
124
|
+
const delegatedParent = ownership.delegatedParent(sessionId);
|
|
142
125
|
const result = useEngine(ctx, (engine) => {
|
|
143
126
|
const sqliteHasWorkflowState = engine.hasSessionStarted(sessionId);
|
|
144
127
|
if (!sqliteHasWorkflowState) {
|
|
145
|
-
const parentFailure = parentSafetyFailure(event, ctx);
|
|
128
|
+
const parentFailure = ownership.parentSafetyFailure(event, ctx);
|
|
146
129
|
if (parentFailure !== undefined)
|
|
147
130
|
return {
|
|
148
131
|
type: 'error',
|
|
149
132
|
output: parentFailure,
|
|
150
133
|
};
|
|
151
134
|
}
|
|
152
|
-
if (
|
|
135
|
+
if (delegatedParent !== undefined && !sqliteHasWorkflowState)
|
|
136
|
+
return {
|
|
137
|
+
type: 'error',
|
|
138
|
+
output: `Pi parent session ${delegatedParent} has no persisted workflow.`,
|
|
139
|
+
};
|
|
140
|
+
if (!inheritedWorkflowState && sessionHasWorkflowMarker && !activeBranchHasWorkflowMarker)
|
|
153
141
|
return {
|
|
154
142
|
type: 'error',
|
|
155
143
|
output: `The active Pi branch does not contain this session's ${PI_WORKFLOW_MARKER_CUSTOM_TYPE} marker.`,
|
|
156
144
|
};
|
|
157
|
-
if (activeBranchHasWorkflowMarker !== sqliteHasWorkflowState)
|
|
145
|
+
if (!inheritedWorkflowState && activeBranchHasWorkflowMarker !== sqliteHasWorkflowState)
|
|
158
146
|
return {
|
|
159
147
|
type: 'error',
|
|
160
148
|
output: `Pi transcript and SQLite workflow state disagree for session ${sessionId}.`,
|
|
@@ -243,7 +231,7 @@ export function createPiWorkflowExtension(config) {
|
|
|
243
231
|
}
|
|
244
232
|
sessionStartsById.set(session.sessionId, event);
|
|
245
233
|
try {
|
|
246
|
-
const hasPersistedWorkflow =
|
|
234
|
+
const hasPersistedWorkflow = ownership.hasPersistedWorkflow(session.sessionId);
|
|
247
235
|
const hasTranscriptWorkflow = hasPiWorkflowMarker(ctx.sessionManager.getEntries());
|
|
248
236
|
if (!hasPersistedWorkflow && !hasTranscriptWorkflow) {
|
|
249
237
|
initializationBySession.set(session.sessionId, { type: 'inactive' });
|
|
@@ -317,7 +305,7 @@ export function createPiWorkflowExtension(config) {
|
|
|
317
305
|
const result = useEngine(ctx, (engine) => engine.checkStopping(session.sessionId, 'stop'));
|
|
318
306
|
if (result.type === 'blocked' && ctx.isIdle() && !ctx.hasPendingMessages()) {
|
|
319
307
|
recoveredAssistantBySession.set(session.sessionId, settlement.id);
|
|
320
|
-
pi.sendUserMessage(
|
|
308
|
+
pi.sendUserMessage(formatStopPreventionMessage(result.output, config.stopPreventionMessage));
|
|
321
309
|
}
|
|
322
310
|
}
|
|
323
311
|
catch (error) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ExtensionContext, SessionStartEvent } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import { type SqliteEventStore } from '@nt-ai-lab/deterministic-agent-workflow-event-store';
|
|
3
|
+
interface PiWorkflowSessionOwnership {
|
|
4
|
+
delegatedParent(sessionId: string, store?: SqliteEventStore): string | undefined;
|
|
5
|
+
hasPersistedWorkflow(sessionId: string): boolean;
|
|
6
|
+
usesInheritedWorkflow(sessionId: string): boolean;
|
|
7
|
+
parentSafetyFailure(event: SessionStartEvent, ctx: ExtensionContext): string | undefined;
|
|
8
|
+
requireAccess(store: SqliteEventStore, sessionId: string): void;
|
|
9
|
+
}
|
|
10
|
+
/** @riviere-role cli-entrypoint */
|
|
11
|
+
export declare function createPiWorkflowSessionOwnership(databasePath: string): PiWorkflowSessionOwnership;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createStore, } from '@nt-ai-lab/deterministic-agent-workflow-event-store';
|
|
2
|
+
import { resolvePiMainSessionId } from '../../../platform/domain/pi-main-session.js';
|
|
3
|
+
import { readPiSessionMetadata } from '../../../platform/infra/external-clients/pi/pi-session-file.js';
|
|
4
|
+
/** @riviere-role cli-entrypoint */
|
|
5
|
+
export function createPiWorkflowSessionOwnership(databasePath) {
|
|
6
|
+
const delegatedParent = (sessionId, existingStore) => {
|
|
7
|
+
const store = existingStore ?? createStore(databasePath);
|
|
8
|
+
try {
|
|
9
|
+
if (store.hasSessionStarted(sessionId))
|
|
10
|
+
return undefined;
|
|
11
|
+
const mainSessionId = resolvePiMainSessionId(sessionId);
|
|
12
|
+
return mainSessionId === sessionId ? undefined : mainSessionId;
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
if (existingStore === undefined)
|
|
16
|
+
store.db.close();
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const requireParent = (store, parentSessionId) => {
|
|
20
|
+
if (!store.hasSessionStarted(parentSessionId)) {
|
|
21
|
+
throw new TypeError(`Pi parent session ${parentSessionId} has no persisted workflow.`);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
delegatedParent,
|
|
26
|
+
hasPersistedWorkflow(sessionId) {
|
|
27
|
+
const store = createStore(databasePath);
|
|
28
|
+
try {
|
|
29
|
+
const parentSessionId = delegatedParent(sessionId, store);
|
|
30
|
+
if (parentSessionId !== undefined) {
|
|
31
|
+
requireParent(store, parentSessionId);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
return store.hasSessionStarted(sessionId);
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
store.db.close();
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
usesInheritedWorkflow(sessionId) {
|
|
41
|
+
const store = createStore(databasePath);
|
|
42
|
+
try {
|
|
43
|
+
return delegatedParent(sessionId, store) !== undefined;
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
store.db.close();
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
parentSafetyFailure(event, ctx) {
|
|
50
|
+
const parentSessionFile = ctx.sessionManager.getHeader()?.parentSession;
|
|
51
|
+
if (parentSessionFile === undefined) {
|
|
52
|
+
return event.reason === 'fork' ? 'Forked Pi session has no verifiable parent session file.' : undefined;
|
|
53
|
+
}
|
|
54
|
+
const parent = readPiSessionMetadata(parentSessionFile);
|
|
55
|
+
const store = createStore(databasePath);
|
|
56
|
+
try {
|
|
57
|
+
return store.hasSessionStarted(parent.id) || parent.hasWorkflowMarker
|
|
58
|
+
? `Cannot fork Pi session ${parent.id}: its workflow is active.`
|
|
59
|
+
: undefined;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
store.db.close();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
requireAccess(store, sessionId) {
|
|
66
|
+
const parentSessionId = delegatedParent(sessionId, store);
|
|
67
|
+
if (parentSessionId !== undefined) {
|
|
68
|
+
requireParent(store, parentSessionId);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { createPiWorkflowExtension, PI_IDLE_RECOVERY_MESSAGE, PI_SESSION_BRANCH_BLOCK_MESSAGE, } from './features/pi-extension/entrypoint/pi-workflow-extension';
|
|
2
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';
|
|
3
5
|
export { PiTranscriptReader } from './platform/infra/external-clients/pi/pi-transcript-reader';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { createPiWorkflowExtension, PI_IDLE_RECOVERY_MESSAGE, PI_SESSION_BRANCH_BLOCK_MESSAGE, } from './features/pi-extension/entrypoint/pi-workflow-extension.js';
|
|
2
|
+
export { resolvePiMainSessionId } from './platform/domain/pi-main-session.js';
|
|
3
|
+
export { refreshPiContextWindow } from './platform/infra/external-clients/pi/pi-context-window.js';
|
|
2
4
|
export { PiTranscriptReader } from './platform/infra/external-clients/pi/pi-transcript-reader.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const PI_SUBAGENT_PARENT_SESSION = 'PI_SUBAGENT_PARENT_SESSION';
|
|
2
|
+
/** @riviere-role domain-service */
|
|
3
|
+
export function resolvePiMainSessionId(currentSessionId, environment = process.env) {
|
|
4
|
+
const current = currentSessionId.trim();
|
|
5
|
+
if (current.length === 0)
|
|
6
|
+
throw new TypeError('Pi returned an empty session UUID.');
|
|
7
|
+
const rawParent = environment[PI_SUBAGENT_PARENT_SESSION];
|
|
8
|
+
if (rawParent === undefined)
|
|
9
|
+
return current;
|
|
10
|
+
const parent = rawParent.trim();
|
|
11
|
+
if (parent.length === 0) {
|
|
12
|
+
throw new TypeError(`${PI_SUBAGENT_PARENT_SESSION} must contain a non-empty session UUID.`);
|
|
13
|
+
}
|
|
14
|
+
return parent;
|
|
15
|
+
}
|
|
@@ -26,9 +26,11 @@ export type PiSessionIdResult = {
|
|
|
26
26
|
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
27
|
readonly workflowDefinition: WorkflowDefinition<TWorkflow, TState, TDeps, TStateName, TOperation, TTransitionContext>;
|
|
28
28
|
readonly routes: RouteMap<TWorkflow, TState>;
|
|
29
|
+
readonly unknownCommandMessage: string;
|
|
29
30
|
readonly buildWorkflowDeps: (platform: PlatformContext) => TDeps;
|
|
30
31
|
readonly pluginRoot: string;
|
|
31
32
|
readonly databasePath?: string;
|
|
32
33
|
readonly commandName?: string;
|
|
33
34
|
readonly toolName?: string;
|
|
35
|
+
readonly stopPreventionMessage?: string;
|
|
34
36
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { estimateTokens, } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
/** @riviere-role external-client-service */
|
|
3
|
+
export function refreshPiContextWindow(session, stateInstructions) {
|
|
4
|
+
if (stateInstructions.trim().length === 0) {
|
|
5
|
+
throw new TypeError('Pi context state instructions must not be empty.');
|
|
6
|
+
}
|
|
7
|
+
if (!session.isIdle) {
|
|
8
|
+
throw new TypeError('Cannot refresh Pi context until the session is idle.');
|
|
9
|
+
}
|
|
10
|
+
if (!session.sessionManager.isPersisted()) {
|
|
11
|
+
throw new TypeError('Cannot refresh Pi context without a persisted session.');
|
|
12
|
+
}
|
|
13
|
+
const tokensBefore = session.messages.reduce((total, message) => total + estimateTokens(message), 0);
|
|
14
|
+
const boundary = session.sessionManager.appendCustomEntry('pi-context-window-boundary');
|
|
15
|
+
session.sessionManager.appendCompaction(stateInstructions, boundary, tokensBefore);
|
|
16
|
+
session.agent.state.messages = session.sessionManager.buildSessionContext().messages;
|
|
17
|
+
}
|
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.5-issue-526.ce3247e.0",
|
|
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-engine": "0.4.
|
|
26
|
+
"@nt-ai-lab/deterministic-agent-workflow-event-store": "0.4.8-issue-526.ce3247e.0",
|
|
27
|
+
"@nt-ai-lab/deterministic-agent-workflow-cli": "0.4.8-issue-526.ce3247e.0",
|
|
28
|
+
"@nt-ai-lab/deterministic-agent-workflow-engine": "0.4.8-issue-526.ce3247e.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@earendil-works/pi-coding-agent": "^0.84.4",
|