@jslee124/forge 0.3.0 → 0.3.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 (35) hide show
  1. package/dist/index.js +1177 -90
  2. package/package.json +2 -1
  3. package/resources/docs/en/ARCHITECTURE.md +519 -0
  4. package/resources/docs/en/AUTHENTICATION.md +224 -0
  5. package/resources/docs/en/CLI_UI.md +266 -0
  6. package/resources/docs/en/CONFIGURATION.md +263 -0
  7. package/resources/docs/en/CONTEXT_MANAGEMENT.md +692 -0
  8. package/resources/docs/en/GETTING_STARTED.md +241 -0
  9. package/resources/docs/en/PLUGINS.md +622 -0
  10. package/resources/docs/en/PRODUCT.md +157 -0
  11. package/resources/docs/en/PROJECT_CONTEXT.md +225 -0
  12. package/resources/docs/en/RELEASING.md +94 -0
  13. package/resources/docs/en/SECURITY.md +272 -0
  14. package/resources/docs/en/SESSIONS.md +134 -0
  15. package/resources/docs/en/TROUBLESHOOTING.md +256 -0
  16. package/resources/docs/index.json +24334 -0
  17. package/resources/docs/zh-CN/ARCHITECTURE.md +174 -0
  18. package/resources/docs/zh-CN/AUTHENTICATION.md +96 -0
  19. package/resources/docs/zh-CN/CLI_UI.md +112 -0
  20. package/resources/docs/zh-CN/CONFIGURATION.md +221 -0
  21. package/resources/docs/zh-CN/CONTEXT_MANAGEMENT.md +200 -0
  22. package/resources/docs/zh-CN/GETTING_STARTED.md +193 -0
  23. package/resources/docs/zh-CN/PLUGINS.md +286 -0
  24. package/resources/docs/zh-CN/PRODUCT.md +86 -0
  25. package/resources/docs/zh-CN/PROJECT_CONTEXT.md +130 -0
  26. package/resources/docs/zh-CN/RELEASING.md +86 -0
  27. package/resources/docs/zh-CN/SECURITY.md +92 -0
  28. package/resources/docs/zh-CN/SESSIONS.md +69 -0
  29. package/resources/docs/zh-CN/TROUBLESHOOTING.md +185 -0
  30. package/resources/skills/forge-plugin-creator/SKILL.md +70 -0
  31. package/resources/skills/forge-plugin-creator/references/plugin-api.md +36 -0
  32. package/resources/skills/forge-plugin-creator/templates/index.mjs +30 -0
  33. package/resources/skills/forge-plugin-creator/templates/plugin.json +8 -0
  34. package/resources/skills/forge-plugin-creator/templates/plugin.test-template.ts +14 -0
  35. package/resources/skills/forge-product-help/SKILL.md +16 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jslee124/forge",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "A safe, observable, and evaluable coding agent",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,6 +9,7 @@
9
9
  },
10
10
  "files": [
11
11
  "dist",
12
+ "resources",
12
13
  "README.md",
13
14
  "LICENSE"
14
15
  ],
@@ -0,0 +1,519 @@
1
+ # Architecture
2
+
3
+ 简体中文 · Documentation index
4
+
5
+ ## Status
6
+
7
+ This document describes the current architecture on the `dev` branch and the
8
+ rationale behind its package boundaries. Simplified interface blocks are
9
+ explanatory sketches, not a stable public SDK; checked-in TypeScript types and
10
+ tests remain authoritative.
11
+
12
+ ## Implementation baseline
13
+
14
+ These choices define the current implementation baseline. They may be revisited
15
+ when measured behavior provides contrary evidence.
16
+
17
+ | Area | Initial decision | Reason |
18
+ | --- | --- | --- |
19
+ | Runtime | Node.js 24 LTS | Use a supported LTS runtime and current platform APIs |
20
+ | Package manager | pnpm 11.18.0 | Fast, strict dependency layout with workspace support |
21
+ | Module format | ESM only | Avoid maintaining dual ESM/CommonJS output |
22
+ | Repository shape | pnpm monorepo | Make runtime boundaries visible without separate repositories |
23
+ | Build | TypeScript project references with `tsc -b` | Enforce package direction without an initial bundler |
24
+ | CLI parsing | Commander | Small, mature process command and help parser |
25
+ | Interactive UI | Ink + React | Component rendering and keyboard input without moving runtime logic into the UI |
26
+ | Validation | Zod | Share runtime validation between configuration and tool inputs |
27
+ | Formatting and linting | Biome | One fast tool with a small configuration surface |
28
+ | Testing | Vitest | Fast TypeScript tests and straightforward fakes |
29
+ | First provider | DeepSeek through `@ai-sdk/deepseek` | Prove one provider path before generalizing |
30
+ | Initial model | `deepseek-v4-flash` | Current fast DeepSeek model with tool and thinking support |
31
+ | Process execution | Node.js `spawn`, `shell: false` | Keep program and arguments structured and avoid implicit shell parsing |
32
+
33
+ The root `package.json` is private and pins pnpm through `packageManager`. Every
34
+ workspace package uses `"type": "module"`. Dependency versions are pinned by
35
+ the lockfile rather than copied into design documents, except for the runtime
36
+ and package-manager baseline above.
37
+
38
+ ### Monorepo layout
39
+
40
+ Packages are created when their milestone begins, not as empty placeholders:
41
+
42
+ ```text
43
+ apps/
44
+ `-- cli/ # @forge/cli: parsing, rendering, approval UI
45
+ packages/
46
+ |-- core/ # @forge/core: loop, events, policy contracts
47
+ |-- codex-app-server/ # Official Codex JSON-RPC transport and auth boundary
48
+ |-- model-deepseek/ # @forge/model-deepseek: AI SDK translation
49
+ |-- model-compat/ # Configured OpenAI-compatible route translation
50
+ |-- model-openai/ # @forge/model-openai: Responses API translation
51
+ |-- auth/ # provider-neutral API-key resolution
52
+ |-- persistence/ # session snapshots, JSONL traces, redaction
53
+ |-- plugin-api/ # executable plugin discovery, trust, host, and API v1
54
+ |-- resources/ # non-executable Skill catalog and safe lazy loading
55
+ |-- tools/ # @forge/tools: built-in tool implementations
56
+ `-- config/ # @forge/config: configuration and context loading
57
+ fixtures/ # Small repository tasks used by integration tests
58
+ `-- validation-bug/
59
+ evals/ # Task manifests, graders, trial runner, reports
60
+ ```
61
+
62
+ `evals/` is a private workspace package. Its live runner imports the real CLI
63
+ run boundary, copies one fixture into a fresh temporary workspace, applies a
64
+ narrow test approval channel, persists the normal run trace, and invokes the
65
+ external grader only after the Agent stops. Generated artifacts are ignored
66
+ until a reviewed report is selected for publication.
67
+
68
+ A generic `shared` package is intentionally avoided; each cross-cutting contract
69
+ belongs to the package that owns its behavior.
70
+
71
+ ### CLI and process conventions
72
+
73
+ The initial CLI uses these exit codes:
74
+
75
+ | Code | Meaning |
76
+ | --- | --- |
77
+ | `0` | Run completed and required verification succeeded |
78
+ | `1` | Unrecovered runtime, provider, or tool failure |
79
+ | `2` | Invalid CLI usage or configuration |
80
+ | `3` | Run stopped without success, including a configured limit |
81
+ | `4` | A required action was denied or no approval channel was available |
82
+ | `130` | User cancellation through Ctrl+C |
83
+
84
+ Tool failures may be returned to the model as observations and therefore do not
85
+ immediately determine the process exit code. Only the terminal run status does.
86
+ Ordinary user errors do not print stack traces unless debug output is enabled.
87
+
88
+ ## System context
89
+
90
+ ```text
91
+ User
92
+ |
93
+ v
94
+ CLI
95
+ |
96
+ v
97
+ Agent Runtime
98
+ | | | | |
99
+ v v v v v
100
+ Model Adapter Context Loader Plugin Host Policy Kernel Run Events
101
+ | | | | |
102
+ v v v v v
103
+ Auth Manager Instructions Contributions Tool Executor Terminal + Trace
104
+ |
105
+ v
106
+ AI SDK -> Model Provider
107
+ ```
108
+
109
+ ## Responsibilities
110
+
111
+ ### CLI
112
+
113
+ The CLI is responsible for:
114
+
115
+ - Parsing commands and configuration
116
+ - Managing the persistent interactive session, multi-line editor, slash-command
117
+ completion, and structured `@` file mentions
118
+ - Selecting the workspace
119
+ - Rendering streamed events and readable diffs
120
+ - Asking the user to approve sensitive actions
121
+ - Forwarding cancellation through an `AbortSignal`
122
+ - Choosing an appropriate process exit code
123
+
124
+ The CLI does not own the agent loop or tool implementation logic.
125
+ Commander owns process-level commands, while Ink owns only the interactive
126
+ terminal presentation. React and Ink remain dependencies of `apps/cli` and must
127
+ not cross into `@forge/core`. File mentions carry workspace-relative paths to
128
+ the model; they do not bypass `read_file`, workspace validation, policy, or
129
+ trace events by injecting file contents automatically.
130
+
131
+ Each interactive prompt starts a fresh bounded run and approval-policy instance.
132
+ Only completed user and assistant text is carried into the next prompt. That
133
+ conversation is persisted as a session and can be restored after restart, while
134
+ tool continuation metadata and approvals remain scoped to the run that produced
135
+ them. See Persistent Sessions and Run Traces.
136
+
137
+ ### Agent runtime
138
+
139
+ The runtime owns:
140
+
141
+ - Run state and step count
142
+ - Conversation messages
143
+ - The model/tool execution loop
144
+ - Provider-supplied reasoning blocks
145
+ - Stop conditions
146
+ - Tool-call validation and dispatch
147
+ - Approval-policy checks
148
+ - Project-context assembly
149
+ - Controlled plugin-hook orchestration
150
+ - Event emission
151
+ - Final run status
152
+
153
+ The runtime depends on interfaces for model access, tools, approval, and trace
154
+ persistence. This keeps it independently testable.
155
+
156
+ ### Model adapter
157
+
158
+ The initial adapter uses Vercel AI SDK and `@ai-sdk/deepseek` for streaming and
159
+ tool-call transport. It uses `deepseek-v4-flash` and explicitly enables thinking
160
+ mode so a provider default change cannot silently alter behavior.
161
+
162
+ The DeepSeek adapter selects the OpenAI-compatible Responses transport only for
163
+ `deepseek-v4-flash-vision-exp`. Core requests carry provider-neutral URL or
164
+ base64 image parts; the CLI owns local-file canonicalization, format validation,
165
+ size limits, and encoding. The transport maps those parts to `input_image`
166
+ content while keeping the runtime-owned tool loop and opaque continuation
167
+ contract unchanged. Other DeepSeek models reject attached images before a
168
+ provider call.
169
+
170
+ The model adapter performs exactly one provider turn and maps the AI SDK full
171
+ stream into Forge model events. Forge controls the multi-step loop and does not
172
+ delegate it to `ToolLoopAgent`, `stopWhen`, or another prebuilt agent
173
+ abstraction. AI SDK tool definitions sent to the model do not receive direct
174
+ `execute` callbacks; Forge validates and executes tool calls only after the
175
+ policy kernel records a decision.
176
+
177
+ DeepSeek thinking-mode tool calls require the provider-returned reasoning
178
+ content to be preserved in subsequent tool-result turns. The adapter therefore
179
+ returns an opaque continuation record alongside observable Forge events. The
180
+ core stores and returns that record to the same adapter, but it must not
181
+ reconstruct it from terminal text or discard provider metadata. An integration
182
+ test covers this round trip.
183
+
184
+ When a provider returns reasoning or thinking content, the adapter preserves it
185
+ as a typed response part. The runtime exposes that content to the terminal and
186
+ trace pipeline. It does not invent reasoning for providers that do not return
187
+ it.
188
+
189
+ ### Authentication manager
190
+
191
+ Authentication is separate from model transport. The model adapter asks an
192
+ authentication manager for request credentials instead of reading environment
193
+ variables or token files directly.
194
+
195
+ The native Forge Engine resolves `DEEPSEEK_API_KEY` or `OPENAI_API_KEY` through
196
+ one provider-neutral manager and uses provider-specific AI SDK adapters. The
197
+ separate Codex Engine starts the official Codex App Server over stdio JSON-RPC.
198
+ Forge initiates managed ChatGPT browser or device-code login, but Codex owns the
199
+ OAuth client identity, callback, tokens, persistence, refresh, and logout.
200
+
201
+ Codex App Server is a complete agent runtime rather than a raw model endpoint.
202
+ It therefore remains a separate engine instead of implementing Forge's
203
+ `ModelAdapter`. Forge dynamically reads `model/list`, validates the selected
204
+ reasoning effort, and streams Codex turn events, while Codex owns its tools,
205
+ sandbox, approvals, and conversation state.
206
+
207
+ Forge must not copy client credentials from another application, depend on
208
+ undocumented endpoints as a stable contract, or silently read credentials from
209
+ `~/.codex/auth.json`. See Authentication.
210
+
211
+ ### Project context loader
212
+
213
+ The project context loader first resolves `FORGE_HOME`, defaulting to the
214
+ operating system user's `~/.forge/`. It validates user configuration and then
215
+ resolves the canonical workspace and working directory. Ordinary configuration
216
+ merges from defaults, user configuration, project configuration, environment
217
+ variables, and explicit CLI flags, preserving provenance for every value. The
218
+ configuration schema marks user-only and strictness-only keys so project values
219
+ cannot pass through the ordinary override algorithm.
220
+
221
+ The loader reads optional user instructions from `~/.forge/AGENTS.md`, then
222
+ loads project `AGENTS.md` instructions from the repository root toward the
223
+ working directory, preferring `AGENTS.override.md` at each level. It preserves
224
+ all instruction paths in the run trace.
225
+
226
+ The separate resource boundary discovers bundled, user, and portable `.agents/`
227
+ Skills as bounded metadata, resolves precedence, and exposes registered content
228
+ only through `load_skill`. Discovery does not execute a resource. It also
229
+ discovers Forge-specific `.forge/` configuration. Project-local executable
230
+ plugins under `.forge/plugins/` are handed to the plugin host only after the
231
+ workspace has been explicitly trusted.
232
+
233
+ User configuration may choose a supported permission profile. Project context
234
+ can influence prompts and make policy stricter, but it cannot grant permissions
235
+ or weaken the policy kernel. Secrets are resolved by the authentication manager
236
+ and never from project configuration. See Project Context and Local
237
+ Customization.
238
+
239
+ ### Plugin host
240
+
241
+ The plugin host is an extension boundary, not the security authority. Trusted
242
+ plugins may:
243
+
244
+ - Register custom tools
245
+ - Register user commands
246
+ - Declare bounded host-managed subagent roles
247
+ - Contribute prompt instructions
248
+ - Observe immutable run events
249
+ - Participate in selected lifecycle hooks
250
+ - Make policy decisions stricter
251
+
252
+ The manifest may also declare `network:access`. A registered tool whose risk is
253
+ `network` requires that declaration and is confirmed on every call under both
254
+ implemented permission profiles. This is an application-level review and
255
+ approval boundary, not network isolation; trusted plugin code can still use
256
+ Node.js directly.
257
+
258
+ All custom tool calls still pass through the policy kernel and tool executor.
259
+ Plugins cannot convert a core `deny` into `allow` or bypass an approval request.
260
+
261
+ A subagent declaration becomes a `model`-risk parent tool. The host—not the
262
+ plugin—creates the child adapter, isolated conversation, inherited policy and
263
+ approval channel, shared budgets, cancellation path, bounded result, and linked
264
+ trace. Child tool selection excludes every subagent tool, so delegation depth
265
+ is one. The active parent model is inherited; cross-model routing and resumable
266
+ child sessions are not part of this contract.
267
+
268
+ An in-process JavaScript plugin is trusted local code and can use Node.js APIs
269
+ directly. API-level capability declarations do not create real isolation. Strong
270
+ plugin isolation requires a separate process or operating-system sandbox and is
271
+ deferred.
272
+
273
+ ### Tools
274
+
275
+ Every tool has:
276
+
277
+ - A unique name
278
+ - A concise model-facing description
279
+ - A Zod input schema
280
+ - An execution function
281
+ - A risk classification
282
+ - A structured result
283
+
284
+ The native tools and checked-in extension examples are:
285
+
286
+ | Tool | Responsibility | Initial risk |
287
+ | --- | --- | --- |
288
+ | `list_files` | List a bounded part of the workspace | Read-only |
289
+ | `read_file` | Read a workspace file with output limits | Read-only |
290
+ | `search` | Search text within the workspace | Read-only |
291
+ | `create_file` | Exclusively create a new UTF-8 workspace file | Write |
292
+ | `apply_patch` | Apply a structured file change | Write |
293
+ | `run_command` | Spawn a program with structured arguments and limits | Variable |
294
+ | `web_search` (example plugin) | Search through a configured or fallback public provider | Network |
295
+ | `web_fetch` (example plugin) | Fetch bounded readable public HTTP(S) text | Network |
296
+ | `delegate_code_review` (example plugin) | Run an isolated read-only review role | Model |
297
+
298
+ Tools receive an explicit execution context instead of reading global process
299
+ state. The context includes the workspace root, abort signal, limits, and event
300
+ emitter.
301
+
302
+ ### Approval policy
303
+
304
+ The policy evaluates an action before execution and returns one of:
305
+
306
+ ```text
307
+ allow Execute without user interaction
308
+ confirm Ask the user before execution
309
+ deny Do not execute
310
+ ```
311
+
312
+ The policy considers:
313
+
314
+ - The canonical target path and whether it remains inside the workspace
315
+ - Whether an operation changes files
316
+ - Whether a command is destructive or otherwise sensitive
317
+ - Whether configured time, output, or call limits have been reached
318
+ - Whether an approval UI is available
319
+
320
+ The default policy is:
321
+
322
+ | Action | Default decision |
323
+ | --- | --- |
324
+ | Read, list, or search inside the workspace | Allow |
325
+ | First write inside the workspace | Confirm |
326
+ | Later workspace writes in the approved run scope | Allow |
327
+ | Any process command | Confirm |
328
+ | Any registered network tool | Confirm |
329
+ | Built-in file operation outside the workspace | Deny in v0.1 |
330
+ | Approval-required action without an approval channel | Deny |
331
+
332
+ Symlinks must be resolved before the policy decision. A future release may add
333
+ narrow outside-workspace approvals, but v0.1 does not expose that capability.
334
+
335
+ `run_command` accepts a program and argument array and uses Node.js `spawn` with
336
+ `shell: false`; shell expressions such as pipelines and redirection are not a
337
+ v0.1 feature. Starting a process in the workspace still does not confine it to
338
+ that workspace. Until an OS-level sandbox exists, confirmation, timeout, output
339
+ limits, and trace records are safety controls but not filesystem or network
340
+ isolation.
341
+
342
+ This policy is an application safety boundary, not a replacement for a hardened
343
+ operating-system sandbox.
344
+
345
+ ### Hooks, events, and traces
346
+
347
+ Forge separates behavior-changing hooks from immutable observation events:
348
+
349
+ - Lifecycle hooks have specific, typed return values.
350
+ - Policy contributions may change `allow` to `confirm` or `deny`, but never make
351
+ a mandatory decision less strict.
352
+ - `RunEvent` values are immutable observations used by renderers, traces, and
353
+ metrics.
354
+
355
+ Runtime behavior is represented as structured events. Candidate event types
356
+ include:
357
+
358
+ ```text
359
+ run.started
360
+ model.started
361
+ model.reasoning
362
+ model.completed
363
+ tool.proposed
364
+ tool.approved
365
+ tool.denied
366
+ tool.started
367
+ tool.completed
368
+ tool.failed
369
+ file.changed
370
+ run.completed
371
+ run.failed
372
+ run.cancelled
373
+ ```
374
+
375
+ Terminal rendering and JSONL persistence consume the same event stream. This
376
+ prevents the user-visible activity and stored trace from becoming two unrelated
377
+ systems.
378
+
379
+ Trace files must not store API keys or other known secrets.
380
+
381
+ ### Sessions and runs
382
+
383
+ A session is a persistent user conversation. A run is one bounded invocation of
384
+ the agent loop for one prompt. One session can therefore contain multiple runs,
385
+ and each run has its own event trace, limits, policy instance, and terminal
386
+ status.
387
+
388
+ The session store belongs at the application boundary rather than inside the
389
+ model adapter or tool packages. It saves completed user/assistant turns and
390
+ ordered run IDs. It does not serialize provider continuation objects, pending
391
+ approvals, an active child process, or an in-progress tool call.
392
+
393
+ On resume, the CLI validates the saved canonical workspace, reloads current
394
+ configuration and instructions, restores completed conversation messages, and
395
+ starts a new run. This makes recovery deterministic without treating stale
396
+ permission state as authority.
397
+
398
+ ## Core interfaces
399
+
400
+ The following simplified sketches explain the implemented boundaries. They omit
401
+ details and are not a stable public API:
402
+
403
+ ```ts
404
+ interface ModelAdapter {
405
+ stream(
406
+ request: ModelRequest,
407
+ signal: AbortSignal,
408
+ ): AsyncIterable<ModelStreamEvent>;
409
+ }
410
+
411
+ interface AuthenticationManager {
412
+ resolve(provider: string, signal: AbortSignal): Promise<ModelCredential>;
413
+ logout(provider: string): Promise<void>;
414
+ }
415
+
416
+ interface ForgeTool<Input, Output> {
417
+ name: string;
418
+ description: string;
419
+ inputSchema: ZodType<Input>;
420
+ risk: ToolRisk;
421
+ execute(input: Input, context: ToolContext): Promise<ToolResult<Output>>;
422
+ }
423
+
424
+ interface ApprovalPolicy {
425
+ evaluate(action: ProposedAction): Promise<ApprovalDecision>;
426
+ }
427
+
428
+ interface TraceWriter {
429
+ append(event: RunEvent): Promise<void>;
430
+ }
431
+
432
+ interface SessionStore {
433
+ create(workspace: WorkspaceContext): Promise<SessionSnapshot>;
434
+ load(sessionId: string): Promise<SessionSnapshot>;
435
+ list(workspaceRoot: string): Promise<readonly SessionSummary[]>;
436
+ save(snapshot: SessionSnapshot): Promise<void>;
437
+ }
438
+ ```
439
+
440
+ These are design sketches, not stable public APIs.
441
+
442
+ ## Run lifecycle
443
+
444
+ ```text
445
+ created
446
+ |
447
+ v
448
+ running <--------+
449
+ | |
450
+ v |
451
+ awaiting_approval|
452
+ | |
453
+ +-------------+
454
+ |
455
+ +--> completed
456
+ +--> failed
457
+ +--> cancelled
458
+ `--> limit_reached
459
+ ```
460
+
461
+ Only terminal states may end a run. A natural-language claim of success does not
462
+ override a failed verification result recorded by the runtime.
463
+
464
+ ## Dependency direction
465
+
466
+ ```text
467
+ CLI ---------------------> Core interfaces
468
+ Native runtime ----------> Core interfaces
469
+ AI SDK adapter ----------> Core interfaces
470
+ Authentication manager --> Core interfaces
471
+ Project context loader --> Core interfaces
472
+ Tools -------------------> Core interfaces
473
+ Trace implementations ---> Core interfaces
474
+ Plugin host --------------> Core extension interfaces
475
+ ```
476
+
477
+ Milestone 10 keeps context ownership split across these boundaries: core owns
478
+ categories, budget arithmetic, events, and stop decisions; adapters own model
479
+ windows, estimation, overflow classification, and continuation projection;
480
+ persistence owns session-v2 checkpoints; the CLI owns `/context`, `/compact`,
481
+ Codex wrapper budgeting, and inspection rendering. The canonical transcript is
482
+ never replaced by its active model view.
483
+
484
+ Configured compatibility routes follow the same ownership rule. Core treats
485
+ continuation as opaque adapter state; the protocol transport retains AI SDK
486
+ reasoning parts and provider metadata for stateless replay instead of teaching
487
+ the runtime vendor fields. Optional `/models` capability extensions are parsed
488
+ conservatively, while missing metadata remains unknown. Provider-specific
489
+ behavior is never selected by matching the user's route name.
490
+
491
+ The core does not import CLI rendering, a specific provider implementation, a
492
+ plugin implementation, or a future LangChain adapter.
493
+
494
+ ## Testing strategy
495
+
496
+ - Unit tests cover path validation, stop conditions, policy rules, and event state.
497
+ - Policy tests cover external paths, symlinks, missing UI, and decision precedence.
498
+ - Tool tests use temporary workspaces.
499
+ - Runtime tests use deterministic fake model adapters.
500
+ - Integration tests cover AI SDK message and tool-call translation.
501
+ - Authentication tests use fake credentials and App Server transports.
502
+ - Context-loader tests cover hierarchy, overrides, case sensitivity, size limits,
503
+ canonical roots, and provenance
504
+ - Plugin-contract tests prove that hooks cannot weaken core decisions.
505
+ - End-to-end tests use small fixture repositories and external graders.
506
+
507
+ Real model calls should not be required for the default test suite.
508
+
509
+ ## Deferred decisions
510
+
511
+ The following choices remain deferred until a concrete milestone and acceptance
512
+ gate need them:
513
+
514
+ - SQLite schema and migration library
515
+ - HTTP server framework
516
+ - LangChain or LangGraph integration shape
517
+ - Operating-system-level sandboxing
518
+ - Restricted plugin process and capability enforcement
519
+ - Cross-machine session synchronization and retention policy