@ai-sdk/harness-codex 1.0.126 → 1.0.129

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.
@@ -0,0 +1,148 @@
1
+ import { createHash } from 'node:crypto';
2
+ import path from 'node:path';
3
+
4
+ const MATCHER = '^apply_patch$';
5
+ const TIMEOUT_SECONDS = 10;
6
+ const COMMAND =
7
+ "printf '%s\\n' \"Tool 'apply_patch' is inactive due to the HarnessAgent tool filtering policy.\" >&2; exit 2";
8
+
9
+ export type TrustedApplyPatchHook = {
10
+ cliOverrides: string[];
11
+ threadConfig: Record<string, unknown>;
12
+ key: string;
13
+ hash: string;
14
+ command: string;
15
+ };
16
+
17
+ export function createTrustedApplyPatchHook({
18
+ codexConfig,
19
+ }: {
20
+ codexConfig: Record<string, unknown>;
21
+ }): TrustedApplyPatchHook {
22
+ const threadConfig = { ...codexConfig };
23
+ const hooks = { ...asRecord(threadConfig.hooks) };
24
+ delete threadConfig.hooks;
25
+
26
+ for (const [key, value] of Object.entries(threadConfig)) {
27
+ if (!key.startsWith('hooks.')) continue;
28
+ const name = key.slice('hooks.'.length);
29
+ if (name.startsWith('state.')) {
30
+ hooks.state = {
31
+ ...asRecord(hooks.state),
32
+ [name.slice('state.'.length)]: value,
33
+ };
34
+ } else {
35
+ hooks[name] = value;
36
+ }
37
+ delete threadConfig[key];
38
+ }
39
+
40
+ const userGroups = hooks.PreToolUse ?? [];
41
+ if (!Array.isArray(userGroups)) {
42
+ throw new Error('Codex config hooks.PreToolUse must be an array.');
43
+ }
44
+
45
+ const handler = {
46
+ type: 'command',
47
+ command: COMMAND,
48
+ timeout: TIMEOUT_SECONDS,
49
+ async: false,
50
+ };
51
+ const hash = `sha256:${createHash('sha256')
52
+ .update(
53
+ JSON.stringify(
54
+ canonicalize({
55
+ event_name: 'pre_tool_use',
56
+ matcher: MATCHER,
57
+ hooks: [handler],
58
+ }),
59
+ ),
60
+ )
61
+ .digest('hex')}`;
62
+ const sourcePath =
63
+ process.platform === 'win32'
64
+ ? path.win32.resolve('C:\\', '<session-flags>/config.toml')
65
+ : path.posix.resolve('/', '<session-flags>/config.toml');
66
+ const key = `${sourcePath}:pre_tool_use:${userGroups.length}:0`;
67
+
68
+ hooks.PreToolUse = [...userGroups, { matcher: MATCHER, hooks: [handler] }];
69
+ hooks.state = {
70
+ ...asRecord(hooks.state),
71
+ [key]: { enabled: true, trusted_hash: hash },
72
+ };
73
+
74
+ return {
75
+ cliOverrides: [`hooks=${toTomlInline(hooks)}`, 'features.hooks=true'],
76
+ threadConfig,
77
+ key,
78
+ hash,
79
+ command: COMMAND,
80
+ };
81
+ }
82
+
83
+ export function assertTrustedApplyPatchHook({
84
+ response,
85
+ hook,
86
+ }: {
87
+ response: unknown;
88
+ hook: TrustedApplyPatchHook;
89
+ }): void {
90
+ const data = asRecord(response)?.data;
91
+ const entry = Array.isArray(data) && data.length === 1 ? data[0] : null;
92
+ const record = asRecord(entry);
93
+ const matches = Array.isArray(record?.hooks)
94
+ ? record.hooks.filter(item => asRecord(item)?.key === hook.key)
95
+ : [];
96
+ const match = matches.length === 1 ? asRecord(matches[0]) : undefined;
97
+ if (
98
+ !match ||
99
+ match.eventName !== 'preToolUse' ||
100
+ match.handlerType !== 'command' ||
101
+ match.command !== hook.command ||
102
+ match.matcher !== MATCHER ||
103
+ match.currentHash !== hook.hash ||
104
+ match.enabled !== true ||
105
+ match.trustStatus !== 'trusted' ||
106
+ (Array.isArray(record?.errors) && record.errors.length > 0)
107
+ ) {
108
+ throw new Error(
109
+ 'Codex app-server did not load the trusted apply_patch filtering hook.',
110
+ );
111
+ }
112
+ }
113
+
114
+ function canonicalize(value: unknown): unknown {
115
+ if (Array.isArray(value)) return value.map(canonicalize);
116
+ if (value != null && typeof value === 'object') {
117
+ return Object.fromEntries(
118
+ Object.entries(value)
119
+ .sort(([a], [b]) => a.localeCompare(b))
120
+ .map(([key, item]) => [key, canonicalize(item)]),
121
+ );
122
+ }
123
+ return value;
124
+ }
125
+
126
+ function toTomlInline(value: unknown): string {
127
+ if (typeof value === 'string') return JSON.stringify(value);
128
+ if (typeof value === 'boolean') return String(value);
129
+ if (typeof value === 'number' && Number.isFinite(value)) {
130
+ return String(value);
131
+ }
132
+ if (Array.isArray(value)) {
133
+ return `[${value.map(toTomlInline).join(',')}]`;
134
+ }
135
+ if (value != null && typeof value === 'object') {
136
+ return `{${Object.entries(value)
137
+ .filter(([, item]) => item !== undefined)
138
+ .map(([key, item]) => `${JSON.stringify(key)}=${toTomlInline(item)}`)
139
+ .join(',')}}`;
140
+ }
141
+ throw new Error('Codex hook configuration contains an unsupported value.');
142
+ }
143
+
144
+ function asRecord(value: unknown): Record<string, unknown> | undefined {
145
+ return value != null && typeof value === 'object' && !Array.isArray(value)
146
+ ? (value as Record<string, unknown>)
147
+ : undefined;
148
+ }
@@ -0,0 +1,42 @@
1
+ export type CodexBuiltinToolPolicy = {
2
+ readonly disabled: {
3
+ readonly bash: boolean;
4
+ readonly webSearch: boolean;
5
+ readonly apply_patch: boolean;
6
+ readonly view_image: boolean;
7
+ };
8
+ readonly disableEnvironments: boolean;
9
+ readonly denyApplyPatchWithHook: boolean;
10
+ readonly webSearchMode: 'live' | 'disabled';
11
+ };
12
+
13
+ export function resolveCodexBuiltinToolPolicy({
14
+ builtinToolFiltering,
15
+ webSearch,
16
+ }: {
17
+ builtinToolFiltering:
18
+ | { mode: 'allow' | 'deny'; toolNames: string[] }
19
+ | undefined;
20
+ webSearch: boolean | undefined;
21
+ }): CodexBuiltinToolPolicy {
22
+ const included = (toolName: string) =>
23
+ builtinToolFiltering == null ||
24
+ (builtinToolFiltering.mode === 'allow'
25
+ ? builtinToolFiltering.toolNames.includes(toolName)
26
+ : !builtinToolFiltering.toolNames.includes(toolName));
27
+ const disabled = {
28
+ bash: !included('bash'),
29
+ webSearch: !included('webSearch'),
30
+ apply_patch: !included('apply_patch'),
31
+ view_image: !included('view_image'),
32
+ };
33
+ const disableEnvironments =
34
+ disabled.bash && disabled.apply_patch && disabled.view_image;
35
+
36
+ return {
37
+ disabled,
38
+ disableEnvironments,
39
+ denyApplyPatchWithHook: disabled.apply_patch && !disableEnvironments,
40
+ webSearchMode: webSearch && !disabled.webSearch ? 'live' : 'disabled',
41
+ };
42
+ }