@namzu/sdk 60.1.0 → 60.2.1

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.
Files changed (116) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  3. package/dist/bridge/a2a/mapper.js +3 -0
  4. package/dist/bridge/a2a/mapper.js.map +1 -1
  5. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  6. package/dist/bridge/sse/mapper.js +16 -0
  7. package/dist/bridge/sse/mapper.js.map +1 -1
  8. package/dist/contracts/session/api.d.ts +1 -1
  9. package/dist/contracts/session/api.d.ts.map +1 -1
  10. package/dist/manager/session/turn-recorder.d.ts +7 -1
  11. package/dist/manager/session/turn-recorder.d.ts.map +1 -1
  12. package/dist/manager/session/turn-recorder.js +19 -0
  13. package/dist/manager/session/turn-recorder.js.map +1 -1
  14. package/dist/peers/dir.d.ts.map +1 -1
  15. package/dist/peers/dir.js +3 -0
  16. package/dist/peers/dir.js.map +1 -1
  17. package/dist/public-runtime.d.ts +1 -0
  18. package/dist/public-runtime.d.ts.map +1 -1
  19. package/dist/public-runtime.js +1 -0
  20. package/dist/public-runtime.js.map +1 -1
  21. package/dist/runtime/query/events.d.ts.map +1 -1
  22. package/dist/runtime/query/events.js +17 -0
  23. package/dist/runtime/query/events.js.map +1 -1
  24. package/dist/runtime/query/executor.d.ts +23 -0
  25. package/dist/runtime/query/executor.d.ts.map +1 -1
  26. package/dist/runtime/query/executor.js +88 -9
  27. package/dist/runtime/query/executor.js.map +1 -1
  28. package/dist/runtime/query/index.d.ts.map +1 -1
  29. package/dist/runtime/query/index.js +13 -0
  30. package/dist/runtime/query/index.js.map +1 -1
  31. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  32. package/dist/runtime/query/iteration/index.js +3 -2
  33. package/dist/runtime/query/iteration/index.js.map +1 -1
  34. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  35. package/dist/runtime/query/iteration/phases/tool-review.js +6 -0
  36. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  37. package/dist/runtime/query/request-void-turns.d.ts +19 -0
  38. package/dist/runtime/query/request-void-turns.d.ts.map +1 -0
  39. package/dist/runtime/query/request-void-turns.js +26 -0
  40. package/dist/runtime/query/request-void-turns.js.map +1 -0
  41. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  42. package/dist/runtime/query/review-policy.js +12 -2
  43. package/dist/runtime/query/review-policy.js.map +1 -1
  44. package/dist/runtime/query/tooling.d.ts +2 -0
  45. package/dist/runtime/query/tooling.d.ts.map +1 -1
  46. package/dist/runtime/query/tooling.js +1 -0
  47. package/dist/runtime/query/tooling.js.map +1 -1
  48. package/dist/sandbox/provider/local.d.ts.map +1 -1
  49. package/dist/sandbox/provider/local.js +6 -5
  50. package/dist/sandbox/provider/local.js.map +1 -1
  51. package/dist/session/paths.d.ts +15 -22
  52. package/dist/session/paths.d.ts.map +1 -1
  53. package/dist/session/paths.js +19 -46
  54. package/dist/session/paths.js.map +1 -1
  55. package/dist/store/session-log/core.d.ts +43 -0
  56. package/dist/store/session-log/core.d.ts.map +1 -1
  57. package/dist/store/session-log/core.js +190 -3
  58. package/dist/store/session-log/core.js.map +1 -1
  59. package/dist/store/session-log/disk.d.ts +1 -1
  60. package/dist/store/session-log/disk.d.ts.map +1 -1
  61. package/dist/store/session-log/disk.js +1 -0
  62. package/dist/store/session-log/disk.js.map +1 -1
  63. package/dist/store/session-log/index.d.ts +1 -1
  64. package/dist/store/session-log/index.d.ts.map +1 -1
  65. package/dist/store/session-log/index.js +1 -1
  66. package/dist/store/session-log/index.js.map +1 -1
  67. package/dist/tools/builtins/bash-cwd.d.ts.map +1 -1
  68. package/dist/tools/builtins/bash-cwd.js +2 -3
  69. package/dist/tools/builtins/bash-cwd.js.map +1 -1
  70. package/dist/tools/builtins/read-media.js +3 -3
  71. package/dist/tools/builtins/read-media.js.map +1 -1
  72. package/dist/turn/reporter.d.ts.map +1 -1
  73. package/dist/turn/reporter.js +2 -0
  74. package/dist/turn/reporter.js.map +1 -1
  75. package/dist/types/hitl/index.d.ts +8 -0
  76. package/dist/types/hitl/index.d.ts.map +1 -1
  77. package/dist/types/hitl/index.js.map +1 -1
  78. package/dist/types/session/events.d.ts +38 -2
  79. package/dist/types/session/events.d.ts.map +1 -1
  80. package/dist/types/session/events.js +4 -0
  81. package/dist/types/session/events.js.map +1 -1
  82. package/dist/types/session/records.d.ts +10 -10
  83. package/dist/types/session/records.d.ts.map +1 -1
  84. package/dist/types/session/records.js +2 -0
  85. package/dist/types/session/records.js.map +1 -1
  86. package/dist/utils/temp.d.ts +82 -0
  87. package/dist/utils/temp.d.ts.map +1 -0
  88. package/dist/utils/temp.js +196 -0
  89. package/dist/utils/temp.js.map +1 -0
  90. package/package.json +1 -1
  91. package/src/bridge/a2a/mapper.ts +3 -0
  92. package/src/bridge/sse/mapper.ts +16 -0
  93. package/src/contracts/session/api.ts +2 -0
  94. package/src/manager/session/turn-recorder.ts +21 -0
  95. package/src/peers/dir.ts +3 -0
  96. package/src/public-runtime.ts +13 -0
  97. package/src/runtime/query/events.ts +19 -0
  98. package/src/runtime/query/executor.ts +92 -8
  99. package/src/runtime/query/index.ts +12 -0
  100. package/src/runtime/query/iteration/index.ts +3 -2
  101. package/src/runtime/query/iteration/phases/tool-review.ts +6 -0
  102. package/src/runtime/query/request-void-turns.ts +28 -0
  103. package/src/runtime/query/review-policy.ts +13 -2
  104. package/src/runtime/query/tooling.ts +3 -0
  105. package/src/sandbox/provider/local.ts +8 -5
  106. package/src/session/paths.ts +32 -57
  107. package/src/store/session-log/core.ts +235 -3
  108. package/src/store/session-log/disk.ts +7 -1
  109. package/src/store/session-log/index.ts +4 -0
  110. package/src/tools/builtins/bash-cwd.ts +2 -3
  111. package/src/tools/builtins/read-media.ts +3 -3
  112. package/src/turn/reporter.ts +2 -0
  113. package/src/types/hitl/index.ts +8 -0
  114. package/src/types/session/events.ts +41 -1
  115. package/src/types/session/records.ts +2 -0
  116. package/src/utils/temp.ts +250 -0
@@ -0,0 +1,250 @@
1
+ import { createHash } from 'node:crypto'
2
+ import { lstatSync, mkdirSync, mkdtempSync } from 'node:fs'
3
+ import { mkdtemp } from 'node:fs/promises'
4
+ import { tmpdir, userInfo } from 'node:os'
5
+ import { isAbsolute, join, relative, resolve, sep } from 'node:path'
6
+
7
+ /**
8
+ * Namzu's own scratch space: the one place that decides where temporary files
9
+ * go, who may enter, and what a failure to create it says.
10
+ *
11
+ * The base is `NAMZU_TMPDIR` when it is set, otherwise the operating system's
12
+ * temporary directory. Under it, `namzu-<user>` is private to the user (mode
13
+ * 0700, refused when it is a symbolic link, not a directory, owned by someone
14
+ * else, or open to group and others). A failure is never answered by quietly
15
+ * using another directory: it is a {@link TempDirectoryError} that names the
16
+ * directory, the cause and `NAMZU_TMPDIR`.
17
+ */
18
+
19
+ /** The environment variable that moves Namzu's scratch space to another folder or disk. */
20
+ export const NAMZU_TMPDIR_ENV = 'NAMZU_TMPDIR'
21
+
22
+ export type TempDirectoryFailure =
23
+ | 'volume_full'
24
+ | 'quota_exceeded'
25
+ | 'not_writable'
26
+ | 'missing'
27
+ | 'not_absolute'
28
+ | 'refused'
29
+
30
+ /** Namzu's scratch directory cannot be used. `message` is a sentence for a person. */
31
+ export class TempDirectoryError extends Error {
32
+ override readonly name = 'TempDirectoryError'
33
+ constructor(
34
+ message: string,
35
+ readonly reason: TempDirectoryFailure,
36
+ readonly directory: string,
37
+ ) {
38
+ super(message)
39
+ }
40
+ }
41
+
42
+ export interface PrivateTempRootOptions {
43
+ /** Base directory. Default `NAMZU_TMPDIR`, else `os.tmpdir()`. */
44
+ readonly tmpdir?: string
45
+ /** Default `process.env`. */
46
+ readonly env?: NodeJS.ProcessEnv
47
+ /** Default `process.platform`. */
48
+ readonly platform?: NodeJS.Platform
49
+ /**
50
+ * Default `process.getuid`. Pass `undefined` explicitly to model a
51
+ * platform without it (win32).
52
+ */
53
+ readonly getuid?: (() => number) | undefined
54
+ /** Default `os.userInfo().username`. */
55
+ readonly username?: () => string
56
+ }
57
+
58
+ /**
59
+ * The directory under which Namzu keeps its temporary files: `NAMZU_TMPDIR`
60
+ * when set (it must be an absolute path), otherwise `os.tmpdir()`. Nothing is
61
+ * created or checked.
62
+ */
63
+ export function resolveTempBase(env: NodeJS.ProcessEnv = process.env): string {
64
+ const configured = env[NAMZU_TMPDIR_ENV]?.trim()
65
+ if (!configured) return tmpdir()
66
+ if (!isAbsolute(configured)) {
67
+ throw new TempDirectoryError(
68
+ `${NAMZU_TMPDIR_ENV} must be a full path, but it is "${configured}". Set it to a folder on a disk with free space.`,
69
+ 'not_absolute',
70
+ configured,
71
+ )
72
+ }
73
+ return configured
74
+ }
75
+
76
+ /** The sentence for a directory that could not be created or written. */
77
+ function explain(error: unknown, directory: string): TempDirectoryError | undefined {
78
+ const code = (error as NodeJS.ErrnoException | undefined)?.code
79
+ const elsewhere = `Free some space and start again, or set ${NAMZU_TMPDIR_ENV} to a folder on another disk.`
80
+ switch (code) {
81
+ case 'ENOSPC':
82
+ return new TempDirectoryError(
83
+ `The disk is full, so Namzu could not create ${directory}. ${elsewhere}`,
84
+ 'volume_full',
85
+ directory,
86
+ )
87
+ case 'EDQUOT':
88
+ return new TempDirectoryError(
89
+ `Your disk quota is used up, so Namzu could not create ${directory}. ${elsewhere}`,
90
+ 'quota_exceeded',
91
+ directory,
92
+ )
93
+ case 'EACCES':
94
+ case 'EPERM':
95
+ case 'EROFS':
96
+ return new TempDirectoryError(
97
+ `Namzu could not create ${directory} because the folder cannot be written to. Set ${NAMZU_TMPDIR_ENV} to a folder you can write to.`,
98
+ 'not_writable',
99
+ directory,
100
+ )
101
+ case 'ENOENT':
102
+ return new TempDirectoryError(
103
+ `Namzu could not create ${directory} because its parent folder does not exist. Create that folder, or set ${NAMZU_TMPDIR_ENV} to one that does.`,
104
+ 'missing',
105
+ directory,
106
+ )
107
+ default:
108
+ return undefined
109
+ }
110
+ }
111
+
112
+ function refuse(root: string, why: string): TempDirectoryError {
113
+ return new TempDirectoryError(
114
+ `Refusing temporary root ${root}: ${why} Set ${NAMZU_TMPDIR_ENV} to a folder only you can write to.`,
115
+ 'refused',
116
+ root,
117
+ )
118
+ }
119
+
120
+ function sha256Hex(value: string): string {
121
+ return createHash('sha256').update(value).digest('hex')
122
+ }
123
+
124
+ /**
125
+ * The per-user private root, `<base>/namzu-<user>`, created if missing.
126
+ *
127
+ * `<user>` is the numeric uid where the platform has one; otherwise the first
128
+ * 12 hex digits of the SHA-256 of the user name; otherwise `user`. On POSIX
129
+ * the directory is created with mode 0700 and refused if it is a symlink, not
130
+ * a directory, owned by another uid, or open to group or others. On win32
131
+ * `%TEMP%` is already per user: a symlink or junction is refused and the
132
+ * owner check is skipped.
133
+ *
134
+ * @throws {TempDirectoryError} when it cannot be created or is not safe to use.
135
+ */
136
+ /**
137
+ * Where {@link privateTempRoot} lives, without creating or checking anything.
138
+ * For a caller that needs to know a path before it is worth making it.
139
+ */
140
+ export function privateTempRootPath(options: PrivateTempRootOptions = {}): string {
141
+ const getuid = 'getuid' in options ? options.getuid : process.getuid?.bind(process)
142
+ let user = 'user'
143
+ if (typeof getuid === 'function') {
144
+ user = String(getuid())
145
+ } else {
146
+ try {
147
+ const name = (options.username ?? (() => userInfo().username))()
148
+ if (name.length > 0) user = sha256Hex(name).slice(0, 12)
149
+ } catch {
150
+ // No user database entry: fall back to the fixed segment.
151
+ }
152
+ }
153
+ return join(options.tmpdir ?? resolveTempBase(options.env), `namzu-${user}`)
154
+ }
155
+
156
+ export function privateTempRoot(options: PrivateTempRootOptions = {}): string {
157
+ const platform = options.platform ?? process.platform
158
+ const getuid = 'getuid' in options ? options.getuid : process.getuid?.bind(process)
159
+ const root = privateTempRootPath(options)
160
+ try {
161
+ mkdirSync(root, { mode: 0o700 })
162
+ } catch (error) {
163
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw explain(error, root) ?? error
164
+ }
165
+ const entry = lstatSync(root)
166
+ if (entry.isSymbolicLink()) throw refuse(root, 'it is a symbolic link.')
167
+ if (!entry.isDirectory()) throw refuse(root, 'it is not a directory.')
168
+ if (platform !== 'win32' && typeof getuid === 'function') {
169
+ if (entry.uid !== getuid()) throw refuse(root, `it belongs to uid ${entry.uid}.`)
170
+ if ((entry.mode & 0o077) !== 0) {
171
+ throw refuse(
172
+ root,
173
+ `mode ${(entry.mode & 0o777).toString(8)} lets other users in; it must be 700.`,
174
+ )
175
+ }
176
+ }
177
+ return root
178
+ }
179
+
180
+ /** A new empty directory named `<prefix>XXXXXX` inside the private root. */
181
+ export function createTempDirSync(prefix: string, options: PrivateTempRootOptions = {}): string {
182
+ const root = privateTempRoot(options)
183
+ try {
184
+ return mkdtempSync(join(root, prefix))
185
+ } catch (error) {
186
+ throw explain(error, join(root, prefix)) ?? error
187
+ }
188
+ }
189
+
190
+ /** As {@link createTempDirSync}, without blocking the event loop. */
191
+ export async function createTempDir(
192
+ prefix: string,
193
+ options: PrivateTempRootOptions = {},
194
+ ): Promise<string> {
195
+ const root = privateTempRoot(options)
196
+ try {
197
+ return await mkdtemp(join(root, prefix))
198
+ } catch (error) {
199
+ throw explain(error, join(root, prefix)) ?? error
200
+ }
201
+ }
202
+
203
+ /**
204
+ * The path of a file named `name` inside the private root. The file itself is
205
+ * not created; the caller opens it, and a write that fails because the disk is
206
+ * full is the caller's to report.
207
+ */
208
+ export function tempFilePath(name: string, options: PrivateTempRootOptions = {}): string {
209
+ return join(privateTempRoot(options), name)
210
+ }
211
+
212
+ /**
213
+ * Make a session's scratch folder (and the folders above it, mode 0700) and
214
+ * confirm it is safe to treat as a place the session may write freely: it lies
215
+ * inside the private root, and neither it nor any folder between it and the
216
+ * root is a symbolic link, a non-folder or owned by someone else. Made on
217
+ * first use, not before, so a session that never needs it leaves nothing behind.
218
+ *
219
+ * @throws {TempDirectoryError} when it cannot be made or is not safe; the
220
+ * caller then leaves the folder outside the session's roots.
221
+ */
222
+ export function ensureScratchpadDirectory(
223
+ directory: string,
224
+ options: PrivateTempRootOptions = {},
225
+ ): string {
226
+ const root = privateTempRoot(options)
227
+ const target = resolve(directory)
228
+ const inside = relative(root, target)
229
+ if (inside === '' || inside.startsWith('..') || isAbsolute(inside)) {
230
+ throw refuse(target, 'it is not inside the private temporary folder.')
231
+ }
232
+ const getuid = 'getuid' in options ? options.getuid : process.getuid?.bind(process)
233
+ const platform = options.platform ?? process.platform
234
+ let current = root
235
+ for (const part of inside.split(sep)) {
236
+ current = join(current, part)
237
+ try {
238
+ mkdirSync(current, { mode: 0o700 })
239
+ } catch (error) {
240
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw explain(error, current) ?? error
241
+ }
242
+ const entry = lstatSync(current)
243
+ if (entry.isSymbolicLink()) throw refuse(current, 'it is a symbolic link.')
244
+ if (!entry.isDirectory()) throw refuse(current, 'it is not a directory.')
245
+ if (platform !== 'win32' && typeof getuid === 'function' && entry.uid !== getuid()) {
246
+ throw refuse(current, `it belongs to uid ${entry.uid}.`)
247
+ }
248
+ }
249
+ return target
250
+ }