@namzu/sdk 45.0.0 → 46.0.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.
Files changed (281) hide show
  1. package/CHANGELOG.md +166 -0
  2. package/dist/authorization/gate.d.ts +5 -2
  3. package/dist/authorization/gate.d.ts.map +1 -1
  4. package/dist/authorization/gate.js +35 -4
  5. package/dist/authorization/gate.js.map +1 -1
  6. package/dist/authorization/rules.d.ts.map +1 -1
  7. package/dist/authorization/rules.js +56 -6
  8. package/dist/authorization/rules.js.map +1 -1
  9. package/dist/authorization/shell-lexer.d.ts +38 -0
  10. package/dist/authorization/shell-lexer.d.ts.map +1 -1
  11. package/dist/authorization/shell-lexer.js +88 -57
  12. package/dist/authorization/shell-lexer.js.map +1 -1
  13. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  14. package/dist/bridge/a2a/mapper.js +2 -0
  15. package/dist/bridge/a2a/mapper.js.map +1 -1
  16. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  17. package/dist/bridge/sse/mapper.js +1 -0
  18. package/dist/bridge/sse/mapper.js.map +1 -1
  19. package/dist/directory/types.d.ts +2 -0
  20. package/dist/directory/types.d.ts.map +1 -1
  21. package/dist/directory/types.js.map +1 -1
  22. package/dist/manager/resident/outbox.d.ts +4 -4
  23. package/dist/pricing/catalogue.generated.d.ts.map +1 -1
  24. package/dist/pricing/catalogue.generated.js +28 -4
  25. package/dist/pricing/catalogue.generated.js.map +1 -1
  26. package/dist/prompt/coding-agent-doctrine.d.ts +1 -1
  27. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  28. package/dist/prompt/coding-agent-doctrine.js +1 -0
  29. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  30. package/dist/public-runtime.d.ts +6 -3
  31. package/dist/public-runtime.d.ts.map +1 -1
  32. package/dist/public-runtime.js +14 -2
  33. package/dist/public-runtime.js.map +1 -1
  34. package/dist/public-tools.d.ts +7 -2
  35. package/dist/public-tools.d.ts.map +1 -1
  36. package/dist/public-tools.js +15 -2
  37. package/dist/public-tools.js.map +1 -1
  38. package/dist/public-types.d.ts +8 -2
  39. package/dist/public-types.d.ts.map +1 -1
  40. package/dist/registry/tool/callable.d.ts +22 -0
  41. package/dist/registry/tool/callable.d.ts.map +1 -0
  42. package/dist/registry/tool/callable.js +29 -0
  43. package/dist/registry/tool/callable.js.map +1 -0
  44. package/dist/registry/tool/execute.d.ts.map +1 -1
  45. package/dist/registry/tool/execute.js +8 -1
  46. package/dist/registry/tool/execute.js.map +1 -1
  47. package/dist/runtime/query/declined.d.ts +12 -0
  48. package/dist/runtime/query/declined.d.ts.map +1 -0
  49. package/dist/runtime/query/declined.js +12 -0
  50. package/dist/runtime/query/declined.js.map +1 -0
  51. package/dist/runtime/query/executor/tool-call-admission.d.ts +37 -11
  52. package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
  53. package/dist/runtime/query/executor/tool-call-admission.js +38 -12
  54. package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
  55. package/dist/runtime/query/executor.d.ts +7 -3
  56. package/dist/runtime/query/executor.d.ts.map +1 -1
  57. package/dist/runtime/query/executor.js +32 -7
  58. package/dist/runtime/query/executor.js.map +1 -1
  59. package/dist/runtime/query/index.d.ts.map +1 -1
  60. package/dist/runtime/query/index.js +1 -0
  61. package/dist/runtime/query/index.js.map +1 -1
  62. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  63. package/dist/runtime/query/iteration/index.js +7 -0
  64. package/dist/runtime/query/iteration/index.js.map +1 -1
  65. package/dist/runtime/query/iteration/phases/context.d.ts +6 -0
  66. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  67. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  68. package/dist/runtime/query/iteration/phases/handoff.d.ts +22 -0
  69. package/dist/runtime/query/iteration/phases/handoff.d.ts.map +1 -0
  70. package/dist/runtime/query/iteration/phases/handoff.js +65 -0
  71. package/dist/runtime/query/iteration/phases/handoff.js.map +1 -0
  72. package/dist/runtime/query/iteration/phases/index.d.ts +1 -0
  73. package/dist/runtime/query/iteration/phases/index.d.ts.map +1 -1
  74. package/dist/runtime/query/iteration/phases/index.js +1 -0
  75. package/dist/runtime/query/iteration/phases/index.js.map +1 -1
  76. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  77. package/dist/runtime/query/iteration/phases/tool-review.js +8 -3
  78. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  79. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  80. package/dist/runtime/query/resume-pending.js +3 -2
  81. package/dist/runtime/query/resume-pending.js.map +1 -1
  82. package/dist/runtime/query/review-policy.d.ts +43 -0
  83. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  84. package/dist/runtime/query/review-policy.js +63 -19
  85. package/dist/runtime/query/review-policy.js.map +1 -1
  86. package/dist/schedules/cron.d.ts +21 -0
  87. package/dist/schedules/cron.d.ts.map +1 -0
  88. package/dist/schedules/cron.js +167 -0
  89. package/dist/schedules/cron.js.map +1 -0
  90. package/dist/schedules/describe.d.ts +14 -0
  91. package/dist/schedules/describe.d.ts.map +1 -0
  92. package/dist/schedules/describe.js +133 -0
  93. package/dist/schedules/describe.js.map +1 -0
  94. package/dist/schedules/errors.d.ts +11 -0
  95. package/dist/schedules/errors.d.ts.map +1 -0
  96. package/dist/schedules/errors.js +15 -0
  97. package/dist/schedules/errors.js.map +1 -0
  98. package/dist/schedules/evaluate.d.ts +35 -0
  99. package/dist/schedules/evaluate.d.ts.map +1 -0
  100. package/dist/schedules/evaluate.js +158 -0
  101. package/dist/schedules/evaluate.js.map +1 -0
  102. package/dist/schedules/index.d.ts +11 -0
  103. package/dist/schedules/index.d.ts.map +1 -0
  104. package/dist/schedules/index.js +8 -0
  105. package/dist/schedules/index.js.map +1 -0
  106. package/dist/schedules/next-fire.d.ts +50 -0
  107. package/dist/schedules/next-fire.d.ts.map +1 -0
  108. package/dist/schedules/next-fire.js +250 -0
  109. package/dist/schedules/next-fire.js.map +1 -0
  110. package/dist/schedules/spec.d.ts +30 -0
  111. package/dist/schedules/spec.d.ts.map +1 -0
  112. package/dist/schedules/spec.js +169 -0
  113. package/dist/schedules/spec.js.map +1 -0
  114. package/dist/schedules/types.d.ts +144 -0
  115. package/dist/schedules/types.d.ts.map +1 -0
  116. package/dist/schedules/types.js +11 -0
  117. package/dist/schedules/types.js.map +1 -0
  118. package/dist/schedules/tz.d.ts +44 -0
  119. package/dist/schedules/tz.d.ts.map +1 -0
  120. package/dist/schedules/tz.js +141 -0
  121. package/dist/schedules/tz.js.map +1 -0
  122. package/dist/skills/index.d.ts +1 -1
  123. package/dist/skills/index.d.ts.map +1 -1
  124. package/dist/skills/index.js +1 -1
  125. package/dist/skills/index.js.map +1 -1
  126. package/dist/skills/loader.d.ts +16 -3
  127. package/dist/skills/loader.d.ts.map +1 -1
  128. package/dist/skills/loader.js +48 -4
  129. package/dist/skills/loader.js.map +1 -1
  130. package/dist/skills/registry.d.ts +6 -0
  131. package/dist/skills/registry.d.ts.map +1 -1
  132. package/dist/skills/registry.js +1 -0
  133. package/dist/skills/registry.js.map +1 -1
  134. package/dist/tools/builtins/browser-url.d.ts +83 -0
  135. package/dist/tools/builtins/browser-url.d.ts.map +1 -0
  136. package/dist/tools/builtins/browser-url.js +240 -0
  137. package/dist/tools/builtins/browser-url.js.map +1 -0
  138. package/dist/tools/builtins/browser.d.ts +367 -0
  139. package/dist/tools/builtins/browser.d.ts.map +1 -0
  140. package/dist/tools/builtins/browser.js +704 -0
  141. package/dist/tools/builtins/browser.js.map +1 -0
  142. package/dist/tools/builtins/computer-use-coordinates.d.ts +65 -0
  143. package/dist/tools/builtins/computer-use-coordinates.d.ts.map +1 -0
  144. package/dist/tools/builtins/computer-use-coordinates.js +123 -0
  145. package/dist/tools/builtins/computer-use-coordinates.js.map +1 -0
  146. package/dist/tools/builtins/computer-use-image.d.ts +77 -0
  147. package/dist/tools/builtins/computer-use-image.d.ts.map +1 -0
  148. package/dist/tools/builtins/computer-use-image.js +223 -0
  149. package/dist/tools/builtins/computer-use-image.js.map +1 -0
  150. package/dist/tools/builtins/computer-use.d.ts +519 -14
  151. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  152. package/dist/tools/builtins/computer-use.js +1188 -183
  153. package/dist/tools/builtins/computer-use.js.map +1 -1
  154. package/dist/tools/builtins/index.d.ts +2 -1
  155. package/dist/tools/builtins/index.d.ts.map +1 -1
  156. package/dist/tools/builtins/index.js +1 -1
  157. package/dist/tools/builtins/index.js.map +1 -1
  158. package/dist/tools/builtins/skill.d.ts +74 -0
  159. package/dist/tools/builtins/skill.d.ts.map +1 -1
  160. package/dist/tools/builtins/skill.js +214 -159
  161. package/dist/tools/builtins/skill.js.map +1 -1
  162. package/dist/tools/defineTool.d.ts +4 -0
  163. package/dist/tools/defineTool.d.ts.map +1 -1
  164. package/dist/tools/defineTool.js +8 -0
  165. package/dist/tools/defineTool.js.map +1 -1
  166. package/dist/tools/schedules/index.d.ts +5 -0
  167. package/dist/tools/schedules/index.d.ts.map +1 -0
  168. package/dist/tools/schedules/index.js +4 -0
  169. package/dist/tools/schedules/index.js.map +1 -0
  170. package/dist/tools/schedules/loop-tool.d.ts +14 -0
  171. package/dist/tools/schedules/loop-tool.d.ts.map +1 -0
  172. package/dist/tools/schedules/loop-tool.js +81 -0
  173. package/dist/tools/schedules/loop-tool.js.map +1 -0
  174. package/dist/tools/schedules/present.d.ts +16 -0
  175. package/dist/tools/schedules/present.d.ts.map +1 -0
  176. package/dist/tools/schedules/present.js +71 -0
  177. package/dist/tools/schedules/present.js.map +1 -0
  178. package/dist/tools/schedules/prompt-scan.d.ts +17 -0
  179. package/dist/tools/schedules/prompt-scan.d.ts.map +1 -0
  180. package/dist/tools/schedules/prompt-scan.js +92 -0
  181. package/dist/tools/schedules/prompt-scan.js.map +1 -0
  182. package/dist/tools/schedules/schedule-tool.d.ts +17 -0
  183. package/dist/tools/schedules/schedule-tool.d.ts.map +1 -0
  184. package/dist/tools/schedules/schedule-tool.js +418 -0
  185. package/dist/tools/schedules/schedule-tool.js.map +1 -0
  186. package/dist/tools/schedules/types.d.ts +252 -0
  187. package/dist/tools/schedules/types.d.ts.map +1 -0
  188. package/dist/tools/schedules/types.js +11 -0
  189. package/dist/tools/schedules/types.js.map +1 -0
  190. package/dist/types/authorization/index.d.ts +107 -9
  191. package/dist/types/authorization/index.d.ts.map +1 -1
  192. package/dist/types/authorization/index.js +16 -1
  193. package/dist/types/authorization/index.js.map +1 -1
  194. package/dist/types/browser/index.d.ts +280 -0
  195. package/dist/types/browser/index.d.ts.map +1 -0
  196. package/dist/types/browser/index.js +12 -0
  197. package/dist/types/browser/index.js.map +1 -0
  198. package/dist/types/computer-use/index.d.ts +176 -0
  199. package/dist/types/computer-use/index.d.ts.map +1 -1
  200. package/dist/types/computer-use/index.js.map +1 -1
  201. package/dist/types/session/events.d.ts +6 -0
  202. package/dist/types/session/events.d.ts.map +1 -1
  203. package/dist/types/session/events.js.map +1 -1
  204. package/dist/types/session/records.d.ts +23 -0
  205. package/dist/types/session/records.d.ts.map +1 -1
  206. package/dist/types/session/records.js +9 -0
  207. package/dist/types/session/records.js.map +1 -1
  208. package/dist/types/tool/index.d.ts +60 -0
  209. package/dist/types/tool/index.d.ts.map +1 -1
  210. package/dist/types/tool/index.js.map +1 -1
  211. package/dist/types/tool/presentation.d.ts +7 -0
  212. package/dist/types/tool/presentation.d.ts.map +1 -1
  213. package/dist/utils/frontmatter.d.ts +18 -2
  214. package/dist/utils/frontmatter.d.ts.map +1 -1
  215. package/dist/utils/frontmatter.js +13 -3
  216. package/dist/utils/frontmatter.js.map +1 -1
  217. package/dist/utils/id.d.ts +8 -0
  218. package/dist/utils/id.d.ts.map +1 -1
  219. package/dist/utils/id.js +12 -0
  220. package/dist/utils/id.js.map +1 -1
  221. package/package.json +3 -1
  222. package/src/authorization/gate.ts +37 -4
  223. package/src/authorization/rules.ts +55 -7
  224. package/src/authorization/shell-lexer.ts +109 -51
  225. package/src/bridge/a2a/mapper.ts +2 -0
  226. package/src/bridge/sse/mapper.ts +1 -0
  227. package/src/directory/types.ts +2 -0
  228. package/src/pricing/catalogue.generated.ts +28 -4
  229. package/src/pricing/rates.source.json +27 -6
  230. package/src/prompt/coding-agent-doctrine.ts +1 -0
  231. package/src/public-runtime.ts +43 -1
  232. package/src/public-tools.ts +54 -1
  233. package/src/public-types.ts +55 -0
  234. package/src/registry/tool/callable.ts +37 -0
  235. package/src/registry/tool/execute.ts +8 -1
  236. package/src/runtime/query/declined.ts +12 -0
  237. package/src/runtime/query/executor/tool-call-admission.ts +60 -16
  238. package/src/runtime/query/executor.ts +36 -5
  239. package/src/runtime/query/index.ts +1 -0
  240. package/src/runtime/query/iteration/index.ts +8 -0
  241. package/src/runtime/query/iteration/phases/context.ts +6 -0
  242. package/src/runtime/query/iteration/phases/handoff.ts +74 -0
  243. package/src/runtime/query/iteration/phases/index.ts +1 -0
  244. package/src/runtime/query/iteration/phases/tool-review.ts +11 -4
  245. package/src/runtime/query/resume-pending.ts +3 -2
  246. package/src/runtime/query/review-policy.ts +107 -18
  247. package/src/schedules/cron.ts +202 -0
  248. package/src/schedules/describe.ts +138 -0
  249. package/src/schedules/errors.ts +15 -0
  250. package/src/schedules/evaluate.ts +178 -0
  251. package/src/schedules/index.ts +18 -0
  252. package/src/schedules/next-fire.ts +257 -0
  253. package/src/schedules/spec.ts +210 -0
  254. package/src/schedules/types.ts +163 -0
  255. package/src/schedules/tz.ts +155 -0
  256. package/src/skills/index.ts +1 -1
  257. package/src/skills/loader.ts +55 -4
  258. package/src/skills/registry.ts +8 -0
  259. package/src/tools/builtins/browser-url.ts +251 -0
  260. package/src/tools/builtins/browser.ts +817 -0
  261. package/src/tools/builtins/computer-use-coordinates.ts +144 -0
  262. package/src/tools/builtins/computer-use-image.ts +278 -0
  263. package/src/tools/builtins/computer-use.ts +1485 -191
  264. package/src/tools/builtins/index.ts +7 -1
  265. package/src/tools/builtins/skill.ts +304 -156
  266. package/src/tools/defineTool.ts +13 -0
  267. package/src/tools/schedules/index.ts +4 -0
  268. package/src/tools/schedules/loop-tool.ts +85 -0
  269. package/src/tools/schedules/present.ts +88 -0
  270. package/src/tools/schedules/prompt-scan.ts +96 -0
  271. package/src/tools/schedules/schedule-tool.ts +482 -0
  272. package/src/tools/schedules/types.ts +265 -0
  273. package/src/types/authorization/index.ts +81 -2
  274. package/src/types/browser/index.ts +341 -0
  275. package/src/types/computer-use/index.ts +202 -0
  276. package/src/types/session/events.ts +6 -0
  277. package/src/types/session/records.ts +10 -0
  278. package/src/types/tool/index.ts +61 -0
  279. package/src/types/tool/presentation.ts +7 -0
  280. package/src/utils/frontmatter.ts +29 -3
  281. package/src/utils/id.ts +14 -0
@@ -0,0 +1,85 @@
1
+ import { z } from 'zod'
2
+ import type { ToolDefinition } from '../../types/tool/index.js'
3
+ import { defineTool } from '../defineTool.js'
4
+ import { presentLoopCall, presentLoopResult } from './present.js'
5
+ import type { SessionLoopHost } from './types.js'
6
+
7
+ export const SESSION_LOOP_TOOL_NAME = 'session_loop'
8
+
9
+ const inputSchema = z.object({
10
+ action: z.enum(['create', 'list', 'delete']),
11
+ interval: z
12
+ .string()
13
+ .optional()
14
+ .describe('create: how often, e.g. "5m", "1h", or a five-field cron expression'),
15
+ prompt: z.string().min(1).max(4_000).optional().describe('create: the message to send each time'),
16
+ id: z.string().optional().describe('delete: the loop id, or "all"'),
17
+ })
18
+
19
+ /**
20
+ * The `session_loop` tool: re-send a prompt to THIS conversation on an
21
+ * interval, between turns, while the session is open.
22
+ *
23
+ * `create` is an ordinary reviewed call — not exempt from review — so in
24
+ * `prompt` mode the operator sees it before a loop exists; a loop a model
25
+ * created is labelled as such wherever it is shown. `list` and `delete` only
26
+ * read or remove loops.
27
+ */
28
+ export function buildSessionLoopTools(host: SessionLoopHost): ToolDefinition[] {
29
+ return [
30
+ defineTool({
31
+ name: SESSION_LOOP_TOOL_NAME,
32
+ description:
33
+ 'Re-send a prompt to this conversation on an interval while the session stays open (between turns only; stops when the session closes; expires after 7 days). Use only when the user asks for something to repeat. Minimum interval one minute.',
34
+ inputSchema,
35
+ category: 'custom',
36
+ permissions: [],
37
+ readOnly: (input) => input.action !== 'create',
38
+ destructive: false,
39
+ concurrencySafe: false,
40
+ presentCall: presentLoopCall,
41
+ presentResult: presentLoopResult,
42
+ async execute(input) {
43
+ if (input.action === 'list') {
44
+ const loops = host.list()
45
+ return {
46
+ success: true,
47
+ output:
48
+ loops.length === 0
49
+ ? 'No loops in this session.'
50
+ : loops.map((l) => `${l.id} · ${l.schedule} · ${l.prompt.slice(0, 80)}`).join('\n'),
51
+ data: { loops },
52
+ }
53
+ }
54
+ if (input.action === 'delete') {
55
+ if (!input.id) return { success: false, output: '', error: 'delete needs id (or "all").' }
56
+ const stopped = await host.delete(input.id)
57
+ return stopped === 0
58
+ ? { success: false, output: '', error: `No loop has id "${input.id}".` }
59
+ : { success: true, output: `Stopped ${stopped} loop${stopped === 1 ? '' : 's'}.` }
60
+ }
61
+ if (!input.interval || !input.prompt) {
62
+ return { success: false, output: '', error: 'create needs interval and prompt.' }
63
+ }
64
+ try {
65
+ const loop = await host.create({
66
+ interval: input.interval,
67
+ prompt: input.prompt,
68
+ createdBy: 'model',
69
+ })
70
+ return {
71
+ success: true,
72
+ output: `Loop ${loop.id} created: ${loop.schedule}. The operator can stop it with /loop stop ${loop.id}.`,
73
+ data: { id: loop.id },
74
+ }
75
+ } catch (error) {
76
+ return {
77
+ success: false,
78
+ output: '',
79
+ error: error instanceof Error ? error.message : String(error),
80
+ }
81
+ }
82
+ },
83
+ }),
84
+ ]
85
+ }
@@ -0,0 +1,88 @@
1
+ import type { ToolResult } from '../../types/tool/index.js'
2
+ import type { ToolCallView, ToolResultView } from '../../types/tool/presentation.js'
3
+
4
+ /**
5
+ * How the schedule tools read to a person: in words, never ids or JSON.
6
+ */
7
+
8
+ const MAX = 80
9
+
10
+ function oneLine(value: unknown): string {
11
+ if (typeof value !== 'string') return ''
12
+ const flat = value.replace(/\s+/g, ' ').trim()
13
+ return flat.length > MAX ? `${flat.slice(0, MAX - 1)}…` : flat
14
+ }
15
+
16
+ function activity(label: string): ToolCallView {
17
+ return { kind: 'generic', presentation: 'activity', label }
18
+ }
19
+
20
+ const HIDDEN: ToolResultView = { kind: 'generic', label: '', visibility: 'hidden' }
21
+
22
+ export function presentScheduleCall(input: {
23
+ readonly action?: unknown
24
+ readonly name?: unknown
25
+ readonly when?: unknown
26
+ readonly job?: unknown
27
+ }): ToolCallView {
28
+ switch (input.action) {
29
+ case 'create': {
30
+ const name = oneLine(input.name)
31
+ const when = oneLine(input.when)
32
+ return activity(`Propose scheduled job${name ? ` · ${name}` : ''}${when ? ` · ${when}` : ''}`)
33
+ }
34
+ case 'list':
35
+ return activity('List scheduled jobs')
36
+ case 'update':
37
+ return activity(`Change scheduled job · ${oneLine(input.job)}`)
38
+ case 'pause':
39
+ return activity(`Pause scheduled job · ${oneLine(input.job)}`)
40
+ case 'resume':
41
+ return activity(`Resume scheduled job · ${oneLine(input.job)}`)
42
+ case 'delete':
43
+ return activity(`Delete scheduled job · ${oneLine(input.job)}`)
44
+ default:
45
+ return activity('Scheduled jobs')
46
+ }
47
+ }
48
+
49
+ /** The operator answered no on the tool's own screen: not a failure. */
50
+ function operatorCancelled(result: ToolResult): boolean {
51
+ const data = result.data as { cancelled?: unknown } | undefined
52
+ return data?.cancelled === true
53
+ }
54
+
55
+ export function presentScheduleResult(input: unknown, result: ToolResult): ToolResultView {
56
+ if (!result.success && operatorCancelled(result)) {
57
+ const action = (input as { action?: unknown } | null)?.action
58
+ return {
59
+ kind: 'generic',
60
+ outcome: 'cancelled',
61
+ label: action === 'create' ? 'Cancelled — no job was created' : 'Cancelled — nothing changed',
62
+ }
63
+ }
64
+ if (!result.success) return { kind: 'generic', label: oneLine(result.error) || 'Not done' }
65
+ return HIDDEN
66
+ }
67
+
68
+ export function presentLoopCall(input: {
69
+ readonly action?: unknown
70
+ readonly interval?: unknown
71
+ readonly prompt?: unknown
72
+ }): ToolCallView {
73
+ switch (input.action) {
74
+ case 'create':
75
+ return activity(`Loop ${oneLine(input.interval)} · ${oneLine(input.prompt)}`)
76
+ case 'list':
77
+ return activity('List loops')
78
+ case 'delete':
79
+ return activity('Stop loop')
80
+ default:
81
+ return activity('Loops')
82
+ }
83
+ }
84
+
85
+ export function presentLoopResult(_input: unknown, result: ToolResult): ToolResultView {
86
+ if (!result.success) return { kind: 'generic', label: oneLine(result.error) || 'Not done' }
87
+ return HIDDEN
88
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * A tripwire over a scheduled prompt, run when the job is proposed.
3
+ *
4
+ * A scheduled prompt runs later with nobody watching, so a prompt that hides
5
+ * characters, tells the model to ignore its instructions, or reads secrets and
6
+ * sends them somewhere deserves a second look before anyone confirms it.
7
+ * Findings are shown to the person confirming; they never block on their own,
8
+ * because the operator may mean exactly what they wrote.
9
+ */
10
+
11
+ /** Characters that render as nothing or reorder what is shown. */
12
+ /** Invisible and direction-changing code point ranges, inclusive. */
13
+ const INVISIBLE_RANGES: readonly (readonly [number, number])[] = [
14
+ [0x200b, 0x200f],
15
+ [0x202a, 0x202e],
16
+ [0x2060, 0x2064],
17
+ [0x2066, 0x2069],
18
+ [0xfeff, 0xfeff],
19
+ [0x00ad, 0x00ad],
20
+ ]
21
+
22
+ function isInvisible(code: number): boolean {
23
+ return INVISIBLE_RANGES.some(([lo, hi]) => code >= lo && code <= hi)
24
+ }
25
+
26
+ function hasInvisible(text: string): boolean {
27
+ for (const ch of text) if (isInvisible(ch.codePointAt(0) ?? 0)) return true
28
+ return false
29
+ }
30
+
31
+ /** C0 controls other than tab, newline and carriage return, and DEL. */
32
+ function isControl(code: number): boolean {
33
+ return (code < 0x20 && code !== 0x09 && code !== 0x0a && code !== 0x0d) || code === 0x7f
34
+ }
35
+
36
+ function hasControl(text: string): boolean {
37
+ for (let i = 0; i < text.length; i++) if (isControl(text.charCodeAt(i))) return true
38
+ return false
39
+ }
40
+
41
+ const DIRECTIVES: readonly RegExp[] = [
42
+ /\bignore (all |any )?(the )?(previous|prior|above|earlier) (instructions|rules|messages)\b/i,
43
+ /\bdisregard (all |any )?(the )?(previous|prior|above|system) /i,
44
+ /\b(reveal|print|show|repeat) (your|the) (system prompt|instructions)\b/i,
45
+ /\byou are no longer\b/i,
46
+ ]
47
+
48
+ const SECRET_READS: readonly RegExp[] = [
49
+ /~?\/?\.ssh\b|\bid_(rsa|ed25519|ecdsa)\b/i,
50
+ /\.aws\/credentials|\.config\/gcloud|\.kube\/config|\.netrc\b|\.npmrc\b|\.pypirc\b/i,
51
+ /\bcredentials\.json\b|\.env\b/i,
52
+ /\b(printenv|env)\b\s*($|[|;&>])/i,
53
+ /\b[A-Z0-9_]*(API_KEY|SECRET|TOKEN|PASSWORD)\b/,
54
+ ]
55
+
56
+ const EXFILTRATION: readonly RegExp[] = [
57
+ /\b(curl|wget)\b[^\n|]*\|\s*(ba|z|da)?sh\b/i,
58
+ /\bcurl\b[^\n]*\s(-d|--data(-binary|-raw)?|-F|--form|-T|--upload-file|-X\s*(POST|PUT))\b/i,
59
+ /\b(nc|ncat|netcat|socat)\b\s+\S+\s+\d+/i,
60
+ /\bbase64\b[^\n]*\|\s*(curl|wget|nc)\b/i,
61
+ ]
62
+
63
+ /** What the scan found, as sentences for the confirmation screen. Empty when nothing. */
64
+ export function scanSchedulePrompt(prompt: string): string[] {
65
+ const findings: string[] = []
66
+ if (hasInvisible(prompt)) {
67
+ findings.push('The prompt contains invisible or direction-changing characters.')
68
+ }
69
+ if (hasControl(prompt)) findings.push('The prompt contains control characters.')
70
+ if (DIRECTIVES.some((re) => re.test(prompt))) {
71
+ findings.push('The prompt tells the model to ignore or reveal its instructions.')
72
+ }
73
+ if (SECRET_READS.some((re) => re.test(prompt))) {
74
+ findings.push('The prompt mentions credentials, keys or secret files.')
75
+ }
76
+ if (EXFILTRATION.some((re) => re.test(prompt))) {
77
+ findings.push('The prompt pipes a download into a shell or sends data to a remote address.')
78
+ }
79
+ return findings
80
+ }
81
+
82
+ /**
83
+ * The prompt with every invisible and control character made visible as
84
+ * `<U+XXXX>`, so the person confirming sees what the model will read.
85
+ */
86
+ export function revealHiddenCharacters(prompt: string): string {
87
+ let out = ''
88
+ for (const ch of prompt) {
89
+ const code = ch.codePointAt(0) ?? 0
90
+ out +=
91
+ isControl(code) || isInvisible(code)
92
+ ? `<U+${code.toString(16).toUpperCase().padStart(4, '0')}>`
93
+ : ch
94
+ }
95
+ return out
96
+ }