@nt-ai-lab/deterministic-agent-workflow-codex 0.4.1 → 0.4.3
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/codex-code-cli/entrypoint/codex-code-workflow-cli.d.ts +1 -0
- package/dist/features/codex-code-cli/entrypoint/codex-code-workflow-cli.js +66 -7
- package/dist/platform/domain/codex-workflow-cli-types.d.ts +1 -0
- package/dist/platform/infra/external-clients/codex/codex-hook-schemas.js +6 -6
- package/package.json +4 -4
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BaseWorkflowState, RehydratableWorkflow } from '@nt-ai-lab/deterministic-agent-workflow-engine';
|
|
2
2
|
import type { CodexWorkflowCliConfig } from '../../../platform/domain/codex-workflow-cli-types';
|
|
3
|
+
export declare function resolveTranscriptPath(sessionId: string, suppliedPath: string | null, now: () => string): string;
|
|
3
4
|
/** @riviere-role cli-entrypoint */
|
|
4
5
|
export declare function createCodexWorkflowCli<TWorkflow extends RehydratableWorkflow<TState>, TState extends BaseWorkflowState<TStateName>, TDeps, TStateName extends string = string, TOperation extends string = string>(config: CodexWorkflowCliConfig<TWorkflow, TState, TDeps, TStateName, TOperation>): void;
|
|
@@ -1,14 +1,62 @@
|
|
|
1
|
+
import { accessSync, constants, readdirSync, statSync, } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
1
3
|
import { join } from 'node:path';
|
|
2
4
|
import { reduceWorkflowStateFromStoredEvents, WorkflowEngine, } from '@nt-ai-lab/deterministic-agent-workflow-engine';
|
|
3
5
|
import { createPreToolUseHandler, createWorkflowRunner, formatContextInjection, formatDenyDecision, getRepositoryName, } from '@nt-ai-lab/deterministic-agent-workflow-cli';
|
|
4
6
|
import { codexHookInputSchema, codexPreToolUseInputSchema, codexSubagentStartInputSchema, } from '../../../platform/infra/external-clients/codex/codex-hook-schemas.js';
|
|
5
7
|
const EMPTY_TRANSCRIPT_READER = { readMessages: () => [] };
|
|
8
|
+
function requireNonEmptyString(value, name) {
|
|
9
|
+
if (value === undefined)
|
|
10
|
+
throw new TypeError(`${name} must be a non-empty string.`);
|
|
11
|
+
const trimmed = value.trim();
|
|
12
|
+
if (trimmed.length === 0)
|
|
13
|
+
throw new TypeError(`${name} must be a non-empty string.`);
|
|
14
|
+
return trimmed;
|
|
15
|
+
}
|
|
16
|
+
export function resolveTranscriptPath(sessionId, suppliedPath, now) {
|
|
17
|
+
const candidate = suppliedPath === null ? '' : suppliedPath.trim();
|
|
18
|
+
if (candidate.length > 0) {
|
|
19
|
+
try {
|
|
20
|
+
accessSync(candidate, constants.R_OK);
|
|
21
|
+
if (!statSync(candidate).isFile())
|
|
22
|
+
throw new TypeError('not a file');
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new TypeError(`Codex transcript is not a readable file: ${candidate}`);
|
|
26
|
+
}
|
|
27
|
+
return candidate;
|
|
28
|
+
}
|
|
29
|
+
const startedAt = new Date(now());
|
|
30
|
+
const directory = join(homedir(), '.codex', 'sessions', String(startedAt.getUTCFullYear()), String(startedAt.getUTCMonth() + 1).padStart(2, '0'), String(startedAt.getUTCDate()).padStart(2, '0'));
|
|
31
|
+
const suffix = `-${sessionId}.jsonl`;
|
|
32
|
+
const matches = (() => {
|
|
33
|
+
try {
|
|
34
|
+
return readdirSync(directory).filter((name) => name.startsWith('rollout-') && name.endsWith(suffix)).map((name) => join(directory, name));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
throw new TypeError(`Unable to resolve exactly one readable Codex transcript for session ${sessionId} in ${directory}.`);
|
|
38
|
+
}
|
|
39
|
+
})();
|
|
40
|
+
if (matches.length !== 1) {
|
|
41
|
+
throw new TypeError(`Unable to resolve exactly one readable Codex transcript for session ${sessionId} in ${directory}.`);
|
|
42
|
+
}
|
|
43
|
+
const transcriptPath = requireNonEmptyString(matches[0], 'transcriptPath');
|
|
44
|
+
try {
|
|
45
|
+
accessSync(transcriptPath, constants.R_OK);
|
|
46
|
+
if (!statSync(transcriptPath).isFile())
|
|
47
|
+
throw new TypeError('not a file');
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
throw new TypeError(`Unable to resolve exactly one readable Codex transcript for session ${sessionId} in ${directory}.`);
|
|
51
|
+
}
|
|
52
|
+
return transcriptPath;
|
|
53
|
+
}
|
|
6
54
|
/** @riviere-role cli-entrypoint */
|
|
7
55
|
export function createCodexWorkflowCli(config) {
|
|
8
56
|
const root = config.workflowRoot ?? resolveWorkflowRoot();
|
|
9
57
|
const databasePath = resolveDatabasePath(config.processDeps);
|
|
10
58
|
const store = config.processDeps.buildStore(databasePath);
|
|
11
|
-
const now = () => new Date().toISOString();
|
|
59
|
+
const now = config.now ?? (() => new Date().toISOString());
|
|
12
60
|
const engineDeps = {
|
|
13
61
|
store,
|
|
14
62
|
getPluginRoot: () => root,
|
|
@@ -36,7 +84,12 @@ function handleWorkflowCommand(config, engineDeps, root, now, args) {
|
|
|
36
84
|
}
|
|
37
85
|
const [operation, sessionId, ...operationArgs] = args;
|
|
38
86
|
const workflowDeps = buildWorkflowDeps(config, engineDeps.store, root, now, sessionId);
|
|
39
|
-
return createWorkflowRunner(config)([operation, ...operationArgs], engineDeps, workflowDeps, {
|
|
87
|
+
return createWorkflowRunner(config)([operation, ...operationArgs], engineDeps, workflowDeps, {
|
|
88
|
+
getSessionId: () => sessionId,
|
|
89
|
+
getSessionTranscriptPath: () => resolveTranscriptPath(sessionId, null, now),
|
|
90
|
+
getSessionRepository: () => getRepositoryName(process.cwd()),
|
|
91
|
+
getRepositoryRoot: () => process.cwd(),
|
|
92
|
+
});
|
|
40
93
|
}
|
|
41
94
|
function buildWorkflowDeps(config, store, root, now, sessionId) {
|
|
42
95
|
const platform = {
|
|
@@ -65,16 +118,19 @@ function handleHookInvocation(config, engineDeps, root, now) {
|
|
|
65
118
|
const workflowDeps = buildWorkflowDeps(config, engineDeps.store, root, now, parsed.session_id);
|
|
66
119
|
const engine = new WorkflowEngine(config.workflowDefinition, engineDeps, workflowDeps);
|
|
67
120
|
switch (parsed.hook_event_name) {
|
|
68
|
-
case 'SessionStart': return startSession(config, engine, parsed.session_id, parsed.transcript_path, parsed.cwd);
|
|
121
|
+
case 'SessionStart': return startSession(config, engine, parsed.session_id, parsed.transcript_path, parsed.cwd, now);
|
|
69
122
|
case 'PreToolUse': return checkToolUse(config, engine, raw);
|
|
70
123
|
case 'SubagentStart': return registerSubagent(engine, raw);
|
|
71
124
|
case 'Stop': return preventUnsupportedStop(config, engineDeps, parsed.session_id);
|
|
72
125
|
}
|
|
73
126
|
}
|
|
74
|
-
function startSession(config, engine, sessionId, transcriptPath, cwd) {
|
|
127
|
+
function startSession(config, engine, sessionId, transcriptPath, cwd, now) {
|
|
75
128
|
if (!engine.hasSessionStarted(sessionId)) {
|
|
76
|
-
const
|
|
77
|
-
const
|
|
129
|
+
const transcriptFile = resolveTranscriptPath(sessionId, transcriptPath, now);
|
|
130
|
+
const repository = getRepositoryName(cwd);
|
|
131
|
+
if (repository === undefined)
|
|
132
|
+
throw new TypeError('repository must be a non-empty string.');
|
|
133
|
+
const result = engine.startSession(sessionId, transcriptFile, repository);
|
|
78
134
|
if (result.type !== 'success')
|
|
79
135
|
return toRunnerResult(result);
|
|
80
136
|
}
|
|
@@ -124,7 +180,10 @@ function checkPatchPaths(handler, engine, sessionId, toolInput) {
|
|
|
124
180
|
if (paths.length === 0)
|
|
125
181
|
return deny('Cannot determine every file edited by Codex apply_patch');
|
|
126
182
|
for (const path of paths) {
|
|
127
|
-
const result = handler(engine, sessionId, 'Write', {
|
|
183
|
+
const result = handler(engine, sessionId, 'Write', {
|
|
184
|
+
file_path: path,
|
|
185
|
+
command
|
|
186
|
+
});
|
|
128
187
|
if (result.type === 'blocked')
|
|
129
188
|
return toHookResult(result);
|
|
130
189
|
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export const codexHookInputSchema = z.object({
|
|
3
|
-
session_id: z.string().min(1),
|
|
4
|
-
transcript_path: z.string().nullable(),
|
|
5
|
-
cwd: z.string().min(1),
|
|
3
|
+
session_id: z.string().trim().min(1),
|
|
4
|
+
transcript_path: z.string().trim().min(1).nullable(),
|
|
5
|
+
cwd: z.string().trim().min(1),
|
|
6
6
|
hook_event_name: z.enum(['SessionStart', 'PreToolUse', 'SubagentStart', 'Stop']),
|
|
7
7
|
});
|
|
8
8
|
export const codexPreToolUseInputSchema = codexHookInputSchema.extend({
|
|
9
9
|
hook_event_name: z.literal('PreToolUse'),
|
|
10
|
-
tool_name: z.string().min(1),
|
|
10
|
+
tool_name: z.string().trim().min(1),
|
|
11
11
|
tool_input: z.record(z.unknown()),
|
|
12
12
|
});
|
|
13
13
|
export const codexSubagentStartInputSchema = codexHookInputSchema.extend({
|
|
14
14
|
hook_event_name: z.literal('SubagentStart'),
|
|
15
|
-
agent_id: z.string().min(1),
|
|
16
|
-
agent_type: z.string().min(1),
|
|
15
|
+
agent_id: z.string().trim().min(1),
|
|
16
|
+
agent_type: z.string().trim().min(1),
|
|
17
17
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nt-ai-lab/deterministic-agent-workflow-codex",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"zod": "^3.25.76",
|
|
20
|
-
"@nt-ai-lab/deterministic-agent-workflow-cli": "0.4.
|
|
21
|
-
"@nt-ai-lab/deterministic-agent-workflow-
|
|
22
|
-
"@nt-ai-lab/deterministic-agent-workflow-
|
|
20
|
+
"@nt-ai-lab/deterministic-agent-workflow-cli": "0.4.1",
|
|
21
|
+
"@nt-ai-lab/deterministic-agent-workflow-engine": "0.4.1",
|
|
22
|
+
"@nt-ai-lab/deterministic-agent-workflow-event-store": "0.4.1"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|