@raidiant/notifai 10.1.1 → 10.1.2

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 (156) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/commands-core.d.ts +1 -1
  3. package/dist/commands-init.js +9 -6
  4. package/dist/commands-init.js.map +1 -1
  5. package/dist/commands-send-support.js +3 -0
  6. package/dist/commands-send-support.js.map +1 -1
  7. package/dist/commands-skill.js +72 -54
  8. package/dist/commands-skill.js.map +1 -1
  9. package/dist/invocation-context.d.ts +1 -0
  10. package/dist/invocation-context.js +9 -1
  11. package/dist/invocation-context.js.map +1 -1
  12. package/dist/native-skills.d.ts +13 -3
  13. package/dist/native-skills.js +51 -12
  14. package/dist/native-skills.js.map +1 -1
  15. package/dist/node-floor.d.ts +2 -1
  16. package/dist/node-floor.js +9 -5
  17. package/dist/node-floor.js.map +1 -1
  18. package/dist/orca-session-title.d.ts +5 -4
  19. package/dist/orca-session-title.js +32 -19
  20. package/dist/orca-session-title.js.map +1 -1
  21. package/dist/platform.d.ts +11 -17
  22. package/dist/platform.js +20 -23
  23. package/dist/platform.js.map +1 -1
  24. package/dist/release.d.ts +5 -5
  25. package/dist/release.js +5 -5
  26. package/dist/setup-destinations.js +1 -1
  27. package/dist/setup-destinations.js.map +1 -1
  28. package/dist/skill-integrity.d.ts +49 -0
  29. package/dist/skill-integrity.js +154 -0
  30. package/dist/skill-integrity.js.map +1 -0
  31. package/dist/skill-source/manifest.json +16 -0
  32. package/dist/skill-source/notifai/SKILL.md +390 -0
  33. package/dist/skill-source/notifai/references/harness-setup.md +220 -0
  34. package/package.json +11 -14
  35. package/src/atomic-file.ts +0 -198
  36. package/src/claude-wake.test.ts +0 -321
  37. package/src/claude-wake.ts +0 -499
  38. package/src/cli-bin.test.ts +0 -176
  39. package/src/cli-bin.ts +0 -233
  40. package/src/cli-release.test.ts +0 -46
  41. package/src/cli-release.ts +0 -78
  42. package/src/cli-update-contract.ts +0 -9
  43. package/src/cli-update.test.ts +0 -125
  44. package/src/client.test.ts +0 -390
  45. package/src/client.ts +0 -352
  46. package/src/codex-wake.test.ts +0 -254
  47. package/src/codex-wake.ts +0 -301
  48. package/src/command-session.ts +0 -38
  49. package/src/commands-agent-sessions.ts +0 -72
  50. package/src/commands-auth.ts +0 -238
  51. package/src/commands-config.ts +0 -336
  52. package/src/commands-core.ts +0 -312
  53. package/src/commands-devices.ts +0 -180
  54. package/src/commands-doctor.ts +0 -1501
  55. package/src/commands-guidance.ts +0 -181
  56. package/src/commands-harness-context.ts +0 -207
  57. package/src/commands-hook-shape.ts +0 -69
  58. package/src/commands-hooks.ts +0 -2318
  59. package/src/commands-init.ts +0 -1055
  60. package/src/commands-io.ts +0 -108
  61. package/src/commands-logs.ts +0 -243
  62. package/src/commands-project.ts +0 -47
  63. package/src/commands-send-support.test.ts +0 -156
  64. package/src/commands-send-support.ts +0 -311
  65. package/src/commands-send.ts +0 -875
  66. package/src/commands-setup-proof.test.ts +0 -72
  67. package/src/commands-setup-proof.ts +0 -124
  68. package/src/commands-skill.test.ts +0 -44
  69. package/src/commands-skill.ts +0 -204
  70. package/src/commands-sounds.test.ts +0 -106
  71. package/src/commands-sounds.ts +0 -49
  72. package/src/commands-update.ts +0 -180
  73. package/src/commands.test.ts +0 -8297
  74. package/src/commands.ts +0 -77
  75. package/src/config-schema.test.ts +0 -153
  76. package/src/config-schema.ts +0 -349
  77. package/src/config.test.ts +0 -581
  78. package/src/config.ts +0 -475
  79. package/src/credentials.test.ts +0 -403
  80. package/src/credentials.ts +0 -403
  81. package/src/file-lock-process-worker.test.ts +0 -64
  82. package/src/file-lock.test.ts +0 -320
  83. package/src/file-lock.ts +0 -393
  84. package/src/fixtures/hermes-0.20.6-classic-cli-local.json +0 -50
  85. package/src/fixtures/openclaw-2026.7.1-2-agent-local.json +0 -57
  86. package/src/generated-session-label.ts +0 -155
  87. package/src/guidance-content.ts +0 -278
  88. package/src/guidance-render.ts +0 -56
  89. package/src/guidance.test.ts +0 -327
  90. package/src/guidance.ts +0 -233
  91. package/src/harness-integration.test.ts +0 -294
  92. package/src/harnesses.test.ts +0 -149
  93. package/src/harnesses.ts +0 -164
  94. package/src/hook-adapter.test.ts +0 -496
  95. package/src/hook-adapter.ts +0 -424
  96. package/src/hook-input.test.ts +0 -39
  97. package/src/hook-input.ts +0 -72
  98. package/src/hooks.test.ts +0 -5824
  99. package/src/hooks.ts +0 -4011
  100. package/src/install-hooks.test.ts +0 -1614
  101. package/src/install-hooks.ts +0 -1501
  102. package/src/interactive.test.ts +0 -387
  103. package/src/interactive.ts +0 -858
  104. package/src/invocation-context.test.ts +0 -352
  105. package/src/invocation-context.ts +0 -197
  106. package/src/logging-process-worker.test.ts +0 -45
  107. package/src/logging.test.ts +0 -544
  108. package/src/logging.ts +0 -698
  109. package/src/main-run.ts +0 -45
  110. package/src/main.ts +0 -12
  111. package/src/native-skills.test.ts +0 -115
  112. package/src/native-skills.ts +0 -162
  113. package/src/node-floor.test.ts +0 -27
  114. package/src/node-floor.ts +0 -37
  115. package/src/onboarding-docs.test.ts +0 -25
  116. package/src/openclaw-plugin.ts +0 -409
  117. package/src/opencode-plugin.ts +0 -298
  118. package/src/orca-session-title.test.ts +0 -54
  119. package/src/orca-session-title.ts +0 -103
  120. package/src/pack-feedback-slice.test.ts +0 -212
  121. package/src/pack-feedback-slice.ts +0 -187
  122. package/src/platform.test.ts +0 -152
  123. package/src/platform.ts +0 -173
  124. package/src/program.test.ts +0 -317
  125. package/src/program.ts +0 -842
  126. package/src/project-enablement.test.ts +0 -55
  127. package/src/project-enablement.ts +0 -99
  128. package/src/question-caps.test.ts +0 -49
  129. package/src/question-settlement-process.ts +0 -47
  130. package/src/question-timing.ts +0 -56
  131. package/src/readiness.test.ts +0 -25
  132. package/src/readiness.ts +0 -212
  133. package/src/release.ts +0 -51
  134. package/src/send-attempts.test.ts +0 -143
  135. package/src/send-attempts.ts +0 -188
  136. package/src/send.test.ts +0 -28
  137. package/src/send.ts +0 -436
  138. package/src/session-activation.ts +0 -61
  139. package/src/session-end-log-lock-worker.test.ts +0 -35
  140. package/src/session-labels.test.ts +0 -490
  141. package/src/session-labels.ts +0 -474
  142. package/src/setup-destinations.test.ts +0 -41
  143. package/src/setup-destinations.ts +0 -50
  144. package/src/skill.test.ts +0 -426
  145. package/src/sound-ref.test.ts +0 -31
  146. package/src/sound-ref.ts +0 -34
  147. package/src/trust-policy.test.ts +0 -701
  148. package/src/ui/banner.ts +0 -95
  149. package/src/ui/config-view.test.ts +0 -90
  150. package/src/ui/config-view.ts +0 -138
  151. package/src/ui/help.test.ts +0 -16
  152. package/src/ui/help.ts +0 -128
  153. package/src/ui/theme.test.ts +0 -92
  154. package/src/ui/theme.ts +0 -265
  155. package/src/url-policy.ts +0 -399
  156. package/tsconfig.json +0 -10
package/src/file-lock.ts DELETED
@@ -1,393 +0,0 @@
1
- import { randomBytes } from 'node:crypto'
2
- import {
3
- chmodSync,
4
- closeSync,
5
- fstatSync,
6
- lstatSync,
7
- mkdirSync,
8
- openSync,
9
- readdirSync,
10
- renameSync,
11
- rmdirSync,
12
- unlinkSync,
13
- } from 'node:fs'
14
- import path from 'node:path'
15
-
16
- /**
17
- * Serialize a short cross-process filesystem transaction.
18
- *
19
- * Each contender publishes all of its ownership in one unique filename. A
20
- * two-phase bakery lock then orders live contenders by ticket and process
21
- * identity. Because paths are never reused, recovering an entry left by a dead
22
- * process cannot remove a later owner's entry; because process liveness, rather
23
- * than age, decides recovery, a delayed holder is never mistaken for a dead one.
24
- *
25
- * The rendezvous directory lives only as long as it has entries: the last
26
- * contender out removes it, so an idle machine keeps no lock state. That makes
27
- * the directory itself a contended resource. A contender registering while
28
- * another releases can find it gone, half-deleted, or already replaced by a
29
- * third contender, and none of those is a broken lock — so publication retries
30
- * within the caller's deadline and pins its rendezvous only once its own entry
31
- * is in the directory, which is the moment removal becomes impossible.
32
- *
33
- * Acquisition is deliberately bounded because both harness hooks and logging
34
- * must fail open rather than hold an agent turn indefinitely.
35
- */
36
- const FILE_LOCK_WAIT_MS = 1_000
37
- const FILE_LOCK_POLL_MS = 5
38
- const FILE_LOCK_TOKEN_BYTES = 12
39
- const ENTRY_PATTERN = /^(choosing|ticket-(\d+))-(\d+)-([0-9a-f]+)$/
40
- const lockSleep = new Int32Array(new SharedArrayBuffer(4))
41
-
42
- export type FileLockObservation =
43
- /** The rendezvous has been scanned and this contender is about to publish. */
44
- | { phase: 'registering'; entry: string }
45
- | { phase: 'choosing-published'; entry: string }
46
- | { phase: 'stale-entry'; entry: string }
47
- | { phase: 'waiting'; blockers: string[] }
48
-
49
- interface FileLockOptions {
50
- waitMs?: number
51
- observe?: (observation: FileLockObservation) => void
52
- /** Installer transactions report lock replacement after a successful action. */
53
- strictRelease?: boolean
54
- }
55
-
56
- interface FileIdentity {
57
- dev: number
58
- ino: number
59
- }
60
-
61
- /**
62
- * Identity for one published lock entry.
63
- *
64
- * Linux can immediately reuse an inode after an unlinked entry is replaced at
65
- * the same path. The creation/change timestamp keeps that replacement distinct
66
- * even when its device and inode numbers happen to match the removed entry.
67
- */
68
- interface EntryIdentity {
69
- dev: bigint
70
- ino: bigint
71
- ctimeNs: bigint
72
- }
73
-
74
- interface ChoosingEntry {
75
- kind: 'choosing'
76
- name: string
77
- pid: number
78
- token: string
79
- }
80
-
81
- interface TicketEntry {
82
- kind: 'ticket'
83
- name: string
84
- pid: number
85
- token: string
86
- ticket: bigint
87
- }
88
-
89
- type LockEntry = ChoosingEntry | TicketEntry
90
-
91
- function parseEntry(name: string): LockEntry | null {
92
- const match = ENTRY_PATTERN.exec(name)
93
- if (match === null) return null
94
- const pid = Number(match[3])
95
- if (!Number.isSafeInteger(pid) || pid <= 0) return null
96
- const token = match[4]!
97
- if (match[1] === 'choosing') return { kind: 'choosing', name, pid, token }
98
- try {
99
- const ticket = BigInt(match[2]!)
100
- if (ticket <= 0n) return null
101
- return { kind: 'ticket', name, pid, token, ticket }
102
- } catch {
103
- return null
104
- }
105
- }
106
-
107
- function processIsAlive(pid: number): boolean {
108
- if (pid === process.pid) return true
109
- try {
110
- process.kill(pid, 0)
111
- return true
112
- } catch (err) {
113
- const code = (err as NodeJS.ErrnoException).code
114
- if (code === 'ESRCH') return false
115
- // EPERM means the process exists but is owned by somebody else. Unknown
116
- // failures are also treated as live: losing recovery is safer than allowing
117
- // two holders into the transaction.
118
- return true
119
- }
120
- }
121
-
122
- function currentUid(): number | undefined {
123
- return typeof process.getuid === 'function' ? process.getuid() : undefined
124
- }
125
-
126
- function assertOwnedDirectory(directory: string): FileIdentity {
127
- const stat = lstatSync(directory)
128
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
129
- throw new Error(`${directory} is not a regular directory; refusing to use it as a file lock.`)
130
- }
131
- const uid = currentUid()
132
- if (uid !== undefined && stat.uid !== uid) {
133
- throw new Error(`${directory} is owned by uid ${stat.uid}, not the current user.`)
134
- }
135
- return { dev: stat.dev, ino: stat.ino }
136
- }
137
-
138
- /**
139
- * The rendezvous a contender was using is no longer the directory at that path.
140
- *
141
- * Fatal once an entry is published — a contender's own entry makes the
142
- * directory non-empty, so no cooperating releaser can remove it. Before
143
- * publication it means only that a releaser removed an empty rendezvous while
144
- * this contender was registering in it, which is ordinary contention.
145
- */
146
- class RendezvousReplaced extends Error {}
147
-
148
- function assertSameDirectory(directory: string, expected: FileIdentity): void {
149
- const current = lstatSync(directory)
150
- if (
151
- current.isSymbolicLink() ||
152
- !current.isDirectory() ||
153
- current.dev !== expected.dev ||
154
- current.ino !== expected.ino
155
- ) {
156
- throw new RendezvousReplaced(`${directory} changed while the file lock was active.`)
157
- }
158
- }
159
-
160
- function entryIdentity(stat: { dev: bigint; ino: bigint; ctimeNs: bigint }): EntryIdentity {
161
- return {
162
- dev: stat.dev,
163
- ino: stat.ino,
164
- ctimeNs: stat.ctimeNs,
165
- }
166
- }
167
-
168
- function sameEntryIdentity(file: string, expected: EntryIdentity): boolean {
169
- try {
170
- const current = lstatSync(file, { bigint: true })
171
- return (
172
- current.dev === expected.dev &&
173
- current.ino === expected.ino &&
174
- current.ctimeNs === expected.ctimeNs
175
- )
176
- } catch {
177
- return false
178
- }
179
- }
180
-
181
- /** Live protocol entries, pruning only unique paths whose process is gone. */
182
- function liveEntries(
183
- directory: string,
184
- directoryIdentity: FileIdentity,
185
- observe: FileLockOptions['observe'],
186
- ): LockEntry[] {
187
- assertSameDirectory(directory, directoryIdentity)
188
- const live: LockEntry[] = []
189
- for (const name of readdirSync(directory)) {
190
- const entry = parseEntry(name)
191
- if (entry === null) continue
192
- const entryPath = path.join(directory, name)
193
- let stat
194
- try {
195
- stat = lstatSync(entryPath)
196
- } catch (err) {
197
- if ((err as NodeJS.ErrnoException).code === 'ENOENT') continue
198
- throw err
199
- }
200
- const uid = currentUid()
201
- if (stat.isSymbolicLink() || !stat.isFile() || (uid !== undefined && stat.uid !== uid)) {
202
- throw new Error(`${entryPath} is not a current-user-owned regular file-lock entry.`)
203
- }
204
- if (processIsAlive(entry.pid)) {
205
- live.push(entry)
206
- continue
207
- }
208
- observe?.({ phase: 'stale-entry', entry: name })
209
- try {
210
- // Recheck the unique dead owner's inode so delayed recovery cannot reap
211
- // a replacement published at the same path by external interference.
212
- const current = lstatSync(entryPath)
213
- if (current.dev === stat.dev && current.ino === stat.ino) unlinkSync(entryPath)
214
- } catch {
215
- // Another contender already recovered this exact dead owner's unique path.
216
- }
217
- }
218
- return live
219
- }
220
-
221
- function publishEmpty(file: string): EntryIdentity {
222
- const handle = openSync(file, 'wx', 0o600)
223
- try {
224
- return entryIdentity(fstatSync(handle, { bigint: true }))
225
- } finally {
226
- closeSync(handle)
227
- }
228
- }
229
-
230
- /**
231
- * True when a publication attempt lost its rendezvous rather than found a
232
- * broken one.
233
- *
234
- * A releaser removes an empty rendezvous directory the moment the last entry
235
- * goes, so a contender registering at that instant sees the removal from
236
- * whichever side it reached first. All three are the same event: `ENOENT` when
237
- * the directory is already gone, `EINVAL` when APFS reports a directory that is
238
- * still being deleted, and a replaced identity when a third contender recreated
239
- * it first. None of them says anything about the lock's integrity, because a
240
- * contender with no published entry owns nothing yet.
241
- */
242
- function lostTheRendezvous(err: unknown): boolean {
243
- if (err instanceof RendezvousReplaced) return true
244
- const code = (err as NodeJS.ErrnoException).code
245
- return code === 'ENOENT' || code === 'EINVAL'
246
- }
247
-
248
- function publishChoosing(
249
- directory: string,
250
- name: string,
251
- deadline: number,
252
- observe: FileLockOptions['observe'],
253
- ): { file: string; fileIdentity: EntryIdentity; directoryIdentity: FileIdentity } {
254
- const file = path.join(directory, name)
255
- for (;;) {
256
- try {
257
- mkdirSync(directory, { recursive: true, mode: 0o700 })
258
- const registrar = assertOwnedDirectory(directory)
259
- chmodSync(directory, 0o700)
260
- // Recover before registration so a delayed stale cleanup can only name an
261
- // old owner's unique path, never this contender's future one.
262
- liveEntries(directory, registrar, observe)
263
- observe?.({ phase: 'registering', entry: name })
264
- const fileIdentity = publishEmpty(file)
265
- // Pin the rendezvous that holds the entry, not the one scanned before it
266
- // existed. `open` resolves the path atomically, so the entry lands in
267
- // whichever directory the path named at that instant — and from then on
268
- // that directory is non-empty, so no cooperating releaser can remove it.
269
- // Reading the identity here therefore names the contender's real
270
- // rendezvous even when the scanned one was removed and recreated
271
- // underneath it.
272
- const directoryIdentity = assertOwnedDirectory(directory)
273
- if (!sameEntryIdentity(file, fileIdentity)) {
274
- throw new RendezvousReplaced(`${directory} no longer holds ${name}.`)
275
- }
276
- return { file, fileIdentity, directoryIdentity }
277
- } catch (err) {
278
- if (!lostTheRendezvous(err)) throw err
279
- // Recreate it and register again; the deadline, not the number of losses,
280
- // decides when contention becomes a failure.
281
- if (Date.now() >= deadline) {
282
- throw new Error(
283
- `timed out publishing file lock contender in ${directory}: ${(err as Error).message}`,
284
- )
285
- }
286
- Atomics.wait(lockSleep, 0, 0, FILE_LOCK_POLL_MS)
287
- }
288
- }
289
- }
290
-
291
- function comesBefore(entry: TicketEntry, ticket: bigint, pid: number, token: string): boolean {
292
- if (entry.ticket !== ticket) return entry.ticket < ticket
293
- if (entry.pid !== pid) return entry.pid < pid
294
- return entry.token < token
295
- }
296
-
297
- export function withFileLock<T>(file: string, action: () => T, options: FileLockOptions = {}): T {
298
- const waitMs = options.waitMs ?? FILE_LOCK_WAIT_MS
299
- const deadline = Date.now() + waitMs
300
- const pid = process.pid
301
- const token = randomBytes(FILE_LOCK_TOKEN_BYTES).toString('hex')
302
- const choosingName = `choosing-${pid}-${token}`
303
- let ownedPath: string | null = null
304
- let ownedIdentity: EntryIdentity | null = null
305
- let directoryIdentity: FileIdentity | null = null
306
- let operationFailed = false
307
-
308
- try {
309
- const published = publishChoosing(file, choosingName, deadline, options.observe)
310
- ownedPath = published.file
311
- ownedIdentity = published.fileIdentity
312
- directoryIdentity = published.directoryIdentity
313
- options.observe?.({ phase: 'choosing-published', entry: choosingName })
314
- let highest = 0n
315
- for (const entry of liveEntries(file, directoryIdentity, options.observe)) {
316
- if (entry.kind === 'ticket' && entry.ticket > highest) highest = entry.ticket
317
- }
318
- const ticket = highest + 1n
319
- const ticketName = `ticket-${ticket}-${pid}-${token}`
320
- const ticketPath = path.join(file, ticketName)
321
-
322
- // The rename is the doorway: observers see either a chooser or its complete
323
- // ticket, never a gap in which a live contender has no published state.
324
- renameSync(ownedPath, ticketPath)
325
- ownedPath = ticketPath
326
- ownedIdentity = entryIdentity(lstatSync(ticketPath, { bigint: true }))
327
-
328
- for (;;) {
329
- const blockers: string[] = []
330
- for (const entry of liveEntries(file, directoryIdentity, options.observe)) {
331
- if (entry.pid === pid && entry.token === token) continue
332
- if (entry.kind === 'choosing' || comesBefore(entry, ticket, pid, token)) {
333
- blockers.push(entry.name)
334
- }
335
- }
336
- if (blockers.length === 0) {
337
- assertSameDirectory(file, directoryIdentity)
338
- return action()
339
- }
340
- options.observe?.({ phase: 'waiting', blockers })
341
- if (Date.now() >= deadline) throw new Error(`timed out waiting for file lock ${file}`)
342
- Atomics.wait(lockSleep, 0, 0, FILE_LOCK_POLL_MS)
343
- }
344
- } catch (err) {
345
- operationFailed = true
346
- throw err
347
- } finally {
348
- let releaseChanged = false
349
- if (ownedPath !== null && ownedIdentity !== null) {
350
- try {
351
- if (sameEntryIdentity(ownedPath, ownedIdentity)) unlinkSync(ownedPath)
352
- else releaseChanged = true
353
- } catch {
354
- // External cleanup cannot turn a release failure into a caller failure.
355
- }
356
- }
357
- try {
358
- if (directoryIdentity !== null) assertSameDirectory(file, directoryIdentity)
359
- rmdirSync(file)
360
- } catch {
361
- // Another contender has published, or external cleanup already removed it.
362
- }
363
- if (releaseChanged && options.strictRelease === true && !operationFailed) {
364
- throw new Error(`${ownedPath ?? file} changed while held; refusing to remove the replacement.`)
365
- }
366
- }
367
- }
368
-
369
- /**
370
- * Serialize a read/merge/write transaction for one user-owned target file.
371
- *
372
- * This is the single installer-facing lock policy: the target's direct parent
373
- * is a real current-user-owned directory, remains the same inode throughout,
374
- * and the bakery rendezvous lives beside the target rather than inside it.
375
- */
376
- export function withTargetFileLock<T>(
377
- target: string,
378
- action: () => T,
379
- options: Omit<FileLockOptions, 'strictRelease'> = {},
380
- ): T {
381
- const parent = path.dirname(target)
382
- mkdirSync(parent, { recursive: true })
383
- const parentIdentity = assertOwnedDirectory(parent)
384
- const lockDirectory = path.join(parent, `.${path.basename(target)}.notifai.lock`)
385
- return withFileLock(
386
- lockDirectory,
387
- () => {
388
- assertSameDirectory(parent, parentIdentity)
389
- return action()
390
- },
391
- { ...options, strictRelease: true },
392
- )
393
- }
@@ -1,50 +0,0 @@
1
- {
2
- "vendor": "NousResearch/hermes-agent",
3
- "release": "0.20.6",
4
- "commit": "5f75ec197b16d1a055688fb9df63ac49586a2230",
5
- "instance": {
6
- "harness": "hermes",
7
- "surface": "classic-cli",
8
- "terminalBackend": "local"
9
- },
10
- "durableSessionId": "20260828_111302_29a404",
11
- "observedToolSubprocessEnv": {
12
- "HERMES_SESSION_ID": "20260828_111302_29a404"
13
- },
14
- "cwd": {
15
- "invocation": "/workspace",
16
- "shellHookCwdIsProcessCwd": true,
17
- "promptSectionCwdRequiresTerminalOverride": true
18
- },
19
- "evidence": {
20
- "nativeHostOs": "macos",
21
- "portableFixtureFamilies": ["posix", "windows"],
22
- "nativeOsUnrun": ["linux", "windows"]
23
- },
24
- "compression": {
25
- "defaultMode": "in-place",
26
- "sessionIdBefore": "20260828_120000_inplace",
27
- "sessionIdAfter": "20260828_120000_inplace",
28
- "legacyRotatingSessionId": "20260828_111114_88ae83"
29
- },
30
- "supported": ["deliberate-send", "source-context-from-invocation-cwd"],
31
- "unsupported": [
32
- "managed-setup",
33
- "lifecycle-activation",
34
- "question-routing"
35
- ],
36
- "deferred": [
37
- "tui",
38
- "gateway",
39
- "api",
40
- "acp-session",
41
- "safe-mode",
42
- "native-linux",
43
- "native-windows",
44
- "live-auto-compression",
45
- "interactive-reset",
46
- "docker-backend",
47
- "ssh-backend",
48
- "other-remote-backends"
49
- ]
50
- }
@@ -1,57 +0,0 @@
1
- {
2
- "vendor": "openclaw/openclaw",
3
- "release": "2026.7.1-2",
4
- "commit": "0790d9f",
5
- "instance": {
6
- "harness": "openclaw",
7
- "surface": "agent-local",
8
- "execHost": "gateway"
9
- },
10
- "durableSessionKey": "agent:main:probe-owner",
11
- "rotatingSessionId": "ae6686a4-d632-4750-b73b-56faec5fff9a",
12
- "observedPluginEnv": {
13
- "NOTIFAI_ACTIVE_HARNESS": "openclaw",
14
- "NOTIFAI_ACTIVE_SESSION_ID": "agent:main:probe-owner"
15
- },
16
- "worker": {
17
- "sessionKeySubstring": ":subagent:",
18
- "provedSessionKey": "agent:main:subagent:probe-worker",
19
- "missingIdentity": "fail-closed-worker"
20
- },
21
- "hooks": {
22
- "proven": {
23
- "activation": "before_prompt_build",
24
- "stop": "agent_end",
25
- "execMarkers": "resolve_exec_env"
26
- },
27
- "unprovenOnThisSurface": {
28
- "presence": "message_received",
29
- "sessionEnd": "session_end"
30
- }
31
- },
32
- "evidence": {
33
- "nativeHostOs": "macos",
34
- "command": "openclaw agent --local",
35
- "nativeOsUnrun": ["linux", "windows"]
36
- },
37
- "supported": [
38
- "managed-setup",
39
- "lifecycle-activation",
40
- "source-context-from-session-key"
41
- ],
42
- "unsupported": [
43
- "question-routing",
44
- "command-stop-as-turn-end",
45
- "2026.2.26-cli"
46
- ],
47
- "deferred": [
48
- "gateway-embedded",
49
- "gateway-remote",
50
- "agent-exec",
51
- "acp-nested",
52
- "exec-sandbox",
53
- "exec-node",
54
- "native-linux",
55
- "native-windows"
56
- ]
57
- }
@@ -1,155 +0,0 @@
1
- const FNV1A_64_OFFSET = 0xcbf29ce484222325n
2
- const FNV1A_64_PRIME = 0x100000001b3n
3
- const WORD_COUNT = 64n
4
-
5
- /** Frozen word lists keep generated fallbacks stable for a given opaque id. */
6
- const ADJECTIVES = [
7
- 'Amber',
8
- 'Arctic',
9
- 'Ash',
10
- 'Azure',
11
- 'Blue',
12
- 'Bold',
13
- 'Bright',
14
- 'Bronze',
15
- 'Calm',
16
- 'Cedar',
17
- 'Clear',
18
- 'Copper',
19
- 'Coral',
20
- 'Crisp',
21
- 'Dawn',
22
- 'Deep',
23
- 'Ember',
24
- 'Fern',
25
- 'Frost',
26
- 'Gentle',
27
- 'Golden',
28
- 'Green',
29
- 'Harbor',
30
- 'Hazel',
31
- 'Indigo',
32
- 'Ivory',
33
- 'Jade',
34
- 'Keen',
35
- 'Lively',
36
- 'Lunar',
37
- 'Maple',
38
- 'Meadow',
39
- 'Mellow',
40
- 'Misty',
41
- 'Noble',
42
- 'Ocean',
43
- 'Olive',
44
- 'Onyx',
45
- 'Quiet',
46
- 'Rapid',
47
- 'Red',
48
- 'River',
49
- 'Royal',
50
- 'Sage',
51
- 'Silver',
52
- 'Solar',
53
- 'Steady',
54
- 'Stone',
55
- 'Sunny',
56
- 'Swift',
57
- 'Teal',
58
- 'Timber',
59
- 'True',
60
- 'Velvet',
61
- 'Verdant',
62
- 'Violet',
63
- 'Warm',
64
- 'Wild',
65
- 'Winter',
66
- 'Wise',
67
- 'Witty',
68
- 'Yellow',
69
- 'Young',
70
- 'Zealous',
71
- ] as const
72
-
73
- const NOUNS = [
74
- 'Alpaca',
75
- 'Antelope',
76
- 'Badger',
77
- 'Bear',
78
- 'Beaver',
79
- 'Bison',
80
- 'Bobcat',
81
- 'Caribou',
82
- 'Cougar',
83
- 'Coyote',
84
- 'Crane',
85
- 'Deer',
86
- 'Dolphin',
87
- 'Dragonfly',
88
- 'Eagle',
89
- 'Elk',
90
- 'Falcon',
91
- 'Finch',
92
- 'Firefly',
93
- 'Fox',
94
- 'Gazelle',
95
- 'Gecko',
96
- 'Hare',
97
- 'Heron',
98
- 'Ibex',
99
- 'Jay',
100
- 'Kestrel',
101
- 'Kingfisher',
102
- 'Koala',
103
- 'Lark',
104
- 'Leopard',
105
- 'Lynx',
106
- 'Magpie',
107
- 'Mantis',
108
- 'Marten',
109
- 'Moose',
110
- 'Osprey',
111
- 'Otter',
112
- 'Owl',
113
- 'Panda',
114
- 'Panther',
115
- 'Pelican',
116
- 'Penguin',
117
- 'Puma',
118
- 'Quail',
119
- 'Rabbit',
120
- 'Raven',
121
- 'Robin',
122
- 'Sable',
123
- 'Salmon',
124
- 'Seal',
125
- 'Shark',
126
- 'Sparrow',
127
- 'Stag',
128
- 'Swan',
129
- 'Tiger',
130
- 'Tortoise',
131
- 'Trout',
132
- 'Turtle',
133
- 'Viper',
134
- 'Whale',
135
- 'Wolf',
136
- 'Wren',
137
- 'Yak',
138
- ] as const
139
-
140
- function fnv1a64(value: string): bigint {
141
- let hash = FNV1A_64_OFFSET
142
- for (const byte of new TextEncoder().encode(value)) {
143
- hash ^= BigInt(byte)
144
- hash = BigInt.asUintN(64, hash * FNV1A_64_PRIME)
145
- }
146
- return hash
147
- }
148
-
149
- /** Deterministic fallback for a session whose work has no semantic title. */
150
- export function generatedSessionLabel(sessionId: string): string {
151
- const hash = fnv1a64(sessionId)
152
- const adjective = ADJECTIVES[Number(hash % WORD_COUNT)]
153
- const noun = NOUNS[Number((hash >> 6n) % WORD_COUNT)]
154
- return `${adjective} ${noun}`
155
- }