chore: remove legacy nix profile resources

This commit is contained in:
Keisuke Hirata 2026-05-30 12:05:34 +09:00
parent 1100ded218
commit b981bbd87b
No known key found for this signature in database
11 changed files with 171 additions and 474 deletions

View File

@ -125,7 +125,7 @@ pub fn user_prompts_dir() -> Option<PathBuf> {
Some(config_dir()?.join("prompts")) Some(config_dir()?.join("prompts"))
} }
/// Root resource directory used for bundled prompts/Nix support files. /// Root resource directory used for bundled prompts, profiles, catalogs, and docs.
pub fn resource_dir() -> Option<PathBuf> { pub fn resource_dir() -> Option<PathBuf> {
if let Some(p) = env_path(RESOURCE_DIR_ENV) { if let Some(p) = env_path(RESOURCE_DIR_ENV) {
return Some(p); return Some(p);

View File

@ -382,7 +382,7 @@ impl ProfileResolver {
.map(str::to_string); .map(str::to_string);
match extension.as_deref() { match extension.as_deref() {
Some("lua") => {} Some("lua") => {}
Some("nix") => return Err(ProfileError::UnsupportedProfileType { path: absolute_path, message: "Nix profile evaluation is not part of the primary Profile path; use a Lua profile or --manifest for a complete Manifest".into() }), Some("nix") => return Err(ProfileError::UnsupportedProfileType { path: absolute_path, message: "Nix profile files are no longer supported; use a Lua profile or --manifest for a complete Manifest".into() }),
other => return Err(ProfileError::UnsupportedProfileType { path: absolute_path, message: format!("unsupported profile extension {}; Lua profiles must end in .lua", other.map_or("<none>".to_string(), |s| format!(".{s}"))) }), other => return Err(ProfileError::UnsupportedProfileType { path: absolute_path, message: format!("unsupported profile extension {}; Lua profiles must end in .lua", other.map_or("<none>".to_string(), |s| format!(".{s}"))) }),
} }
let profile_dir = absolute_path let profile_dir = absolute_path

View File

@ -416,7 +416,7 @@ mod tests {
); );
metadata.resolved_manifest_snapshot = Some(serde_json::json!({ metadata.resolved_manifest_snapshot = Some(serde_json::json!({
"pod": { "name": "profile-pod" }, "pod": { "name": "profile-pod" },
"profile": { "source": { "kind": "path", "path": "/profiles/coder.nix" } } "profile": { "source": { "kind": "path", "path": "/profiles/coder.lua" } }
})); }));
let json = serde_json::to_string(&metadata).unwrap(); let json = serde_json::to_string(&metadata).unwrap();

View File

@ -444,7 +444,7 @@ permission = "write"
#[test] #[test]
fn profile_uses_selected_profile() { fn profile_uses_selected_profile() {
let tmp = TempDir::new().unwrap(); let tmp = TempDir::new().unwrap();
let profile = tmp.path().join("profile.nix"); let profile = tmp.path().join("profile.lua");
let cli = Cli::try_parse_from([ let cli = Cli::try_parse_from([
"insomnia-pod", "insomnia-pod",
"--profile", "--profile",
@ -633,12 +633,12 @@ permission = "write"
fn profile_conflicts_with_manifest_and_restore_modes() { fn profile_conflicts_with_manifest_and_restore_modes() {
let segment_id = session_store::new_segment_id().to_string(); let segment_id = session_store::new_segment_id().to_string();
for args in [ for args in [
vec!["insomnia-pod", "--profile", "p.nix", "--manifest", "m.toml"], vec!["insomnia-pod", "--profile", "p.lua", "--manifest", "m.toml"],
vec!["insomnia-pod", "--profile", "p.nix", "--pod", "agent"], vec!["insomnia-pod", "--profile", "p.lua", "--pod", "agent"],
vec![ vec![
"insomnia-pod", "insomnia-pod",
"--profile", "--profile",
"p.nix", "p.lua",
"--session", "--session",
&segment_id, &segment_id,
], ],
@ -659,7 +659,7 @@ permission = "write"
let cli = Cli::try_parse_from([ let cli = Cli::try_parse_from([
"insomnia-pod", "insomnia-pod",
"--profile", "--profile",
"p.nix", "p.lua",
"--profile-pod-name", "--profile-pod-name",
"agent", "agent",
]) ])

View File

@ -1181,9 +1181,9 @@ mod tests {
#[test] #[test]
fn parse_profile_spawn_mode() { fn parse_profile_spawn_mode() {
match parse_args_from(["--profile", "/profiles/coder.nix"]).unwrap() { match parse_args_from(["--profile", "/profiles/coder.lua"]).unwrap() {
Mode::Spawn { profile } => { Mode::Spawn { profile } => {
assert_eq!(profile, Some("/profiles/coder.nix".to_string())); assert_eq!(profile, Some("/profiles/coder.lua".to_string()));
} }
_ => panic!("expected Spawn mode"), _ => panic!("expected Spawn mode"),
} }
@ -1196,7 +1196,7 @@ mod tests {
( (
vec![ vec![
"--profile".to_string(), "--profile".to_string(),
"p.nix".to_string(), "p.lua".to_string(),
"--resume".to_string(), "--resume".to_string(),
], ],
"--profile can only be used for fresh spawn", "--profile can only be used for fresh spawn",
@ -1204,7 +1204,7 @@ mod tests {
( (
vec![ vec![
"--profile".to_string(), "--profile".to_string(),
"p.nix".to_string(), "p.lua".to_string(),
"--session".to_string(), "--session".to_string(),
segment_id, segment_id,
], ],
@ -1213,7 +1213,7 @@ mod tests {
( (
vec![ vec![
"--profile".to_string(), "--profile".to_string(),
"p.nix".to_string(), "p.lua".to_string(),
"--socket".to_string(), "--socket".to_string(),
"/tmp/insomnia/sock".to_string(), "/tmp/insomnia/sock".to_string(),
], ],
@ -1222,7 +1222,7 @@ mod tests {
( (
vec![ vec![
"--profile".to_string(), "--profile".to_string(),
"p.nix".to_string(), "p.lua".to_string(),
"agent".to_string(), "agent".to_string(),
], ],
"--profile can only be used for fresh spawn", "--profile can only be used for fresh spawn",

View File

@ -655,7 +655,7 @@ mod tests {
r#" r#"
default = "coder" default = "coder"
[profile] [profile]
coder = "profiles/coder.nix" coder = "profiles/coder.lua"
"#, "#,
) )
.unwrap(); .unwrap();
@ -679,7 +679,7 @@ coder = "profiles/coder.nix"
r#" r#"
default = "coder" default = "coder"
[profile.coder] [profile.coder]
path = "profiles/coder.nix" path = "profiles/coder.lua"
description = "Project coder" description = "Project coder"
"#, "#,
) )

View File

@ -31,7 +31,7 @@ insomnia-pod --profile ./coder.lua
insomnia --profile ./coder.lua insomnia --profile ./coder.lua
``` ```
`--profile` accepts an explicit path, `path:<path>`, a discovered profile name, `default`, or a source-qualified name such as `project:coder`, `user:coder`, or `builtin:coder`. Path-like values containing `/`, starting with `.`, or ending in `.lua` are explicit paths. `.nix` paths are no longer the primary profile layer and fail with a diagnostic that points users at Lua profiles or `--manifest`. `--profile` accepts an explicit path, `path:<path>`, a discovered profile name, `default`, or a source-qualified name such as `project:coder`, `user:coder`, or `builtin:coder`. Path-like values containing `/`, starting with `.`, or ending in `.lua` are explicit paths. ``.nix` paths are no longer supported as profiles and fail with a diagnostic that points users at Lua profiles or `--manifest`.
`--profile` conflicts with `insomnia-pod --manifest` and with restore/session/adopt modes. Use `--profile-pod-name <name>` when a launcher needs a creation-time Pod name override without invoking `--pod` restore semantics. Profile evaluation is a creation-time path; Pod resume restores saved Pod state/resolved snapshots rather than re-evaluating the profile source. `--profile` conflicts with `insomnia-pod --manifest` and with restore/session/adopt modes. Use `--profile-pod-name <name>` when a launcher needs a creation-time Pod name override without invoking `--pod` restore semantics. Profile evaluation is a creation-time path; Pod resume restores saved Pod state/resolved snapshots rather than re-evaluating the profile source.

View File

@ -206,11 +206,11 @@ host_a (spawner) host_b (remote)
# 1. session + profile/manifest input を転送 # 1. session + profile/manifest input を転送
ssh insomnia@host-b "mkdir -p ~/workspaces/task-123/store" ssh insomnia@host-b "mkdir -p ~/workspaces/task-123/store"
tar cz session/ | ssh insomnia@host-b "tar xz -C ~/workspaces/task-123/store" tar cz session/ | ssh insomnia@host-b "tar xz -C ~/workspaces/task-123/store"
scp profile.nix insomnia@host-b:~/workspaces/task-123/profile.nix scp profile.lua insomnia@host-b:~/workspaces/task-123/profile.lua
# 2. Pod を起動detach # 2. Pod を起動detach
ssh insomnia@host-b "insomnia-pod --store ~/workspaces/task-123/store \ ssh insomnia@host-b "insomnia-pod --store ~/workspaces/task-123/store \
--profile ~/workspaces/task-123/profile.nix &" --profile ~/workspaces/task-123/profile.lua &"
# 3. socket を tunnel で引っ張る # 3. socket を tunnel で引っ張る
ssh -L /tmp/pod-b.sock:/run/insomnia/task-123/pod.sock insomnia@host-b ssh -L /tmp/pod-b.sock:/run/insomnia/task-123/pod.sock insomnia@host-b

View File

@ -1,385 +1,189 @@
# Pod Factory: カスケード設定とプロンプト資産 # Pod Factory: Profile resolution and prompt assets
`PodFactory` は、複数の層に分かれた `manifest.toml` とプログラマティック `PodFactory` は、選択された Profile または明示的な one-file Manifest から、検証済みの `PodManifest``PromptLoader` を生成する境界である。通常の fresh spawn は profile discovery/default selection を使い、user/project `manifest.toml` の ambient cascade は使わない。
overlay をマージして、検証済みの `PodManifest``PromptLoader` を生成する
ビルダー。これにより Pod 起動ごとに TOML を手書きする必要がなくなる。 `PodManifest` は Pod 起動に必要な完全な runtime recipe で、Pod 名、具体的な scope、解決済みパス、model/provider 設定、worker 設定などを含む。Profile はその前段の再利用可能な recipe template であり、Pod 名や concrete scope authority など runtime-bound な値は resolver が起動入力から埋める。
--- ---
## カスケード層 ## 起動モード
優先順位が低い順(上位ほど下位を上書き): | モード | 入力 | 用途 | ambient manifest cascade |
| 優先度 | 層 | 位置 | 典型的な内容 |
|---|---|---|---| |---|---|---|---|
| 1 | ビルトインのデフォルト | `manifest::defaults` モジュールの `pub const` 群を `PodManifestConfig::builtin_defaults()` が cascade 層として注入 | `tool_output.default_max_bytes = 64 KiB`, `file_upload.max_bytes = 256 KiB` など | | Profile | `--profile <selector>` または省略時 default | 通常の fresh spawn。Lua profile を解決して runtime Manifest を作る | 使わない |
| 2 | ユーザー manifest | `<config_dir>/manifest.toml`(解決ルールは `manifest::paths` | プロバイダ指定、デフォルトモデル、常用ツール設定 | | One-file Manifest | `--manifest <path>` | デバッグ/互換用の完全 Manifest escape hatch | 使わない |
| 3 | プロジェクト manifest | 起動ディレクトリから上方向に探索した最初の `<root>/.insomnia/manifest.toml` | scope、compaction、プロジェクト固有の instruction | | Restore/attach | `--pod` / `--session` 等 | 保存済み Pod state / session snapshot から再開 | profile を再評価しない |
| 4 | プログラマティック overlay | CLI / GUI / 別 Pod からの spawn 等 | `pod.name`、spawn 時の `worker.instruction` のような Pod 固有値 | | SpawnPod internal | hidden `--spawn-config-json` | 親 Pod が子用 config を解決して渡す | 使わない |
デフォルト値はすべて `crates/manifest/src/defaults.rs``pub const` として集約 `profiles.toml` は profile registry/default selection のための UX 設定であり、Pod Manifest 層ではない。Profile の中身へ merge されない。
されており、serde `#[default = "..."]` 経路(`PodManifest` の直接 deserialize
`TryFrom<PodManifestConfig>` 経路cascade 解決)の両方が同じ constants を
参照する。デフォルトを変更するときは `defaults.rs` の 1 行を書き換えるだけで
全経路に反映される。
どの層も TOML スキーマは `PodManifest` と同じ(全フィールド省略可)。 ## Profile resolution
## マージセマンティクス Profile は Lua で書かれる。Rust resolver は selected profile を restricted Lua VM 内で評価し、返り値が Profile-shaped であることを検証してから `PodManifest` に変換する。
| フィールド種別 | 規則 | ```lua
local profile = require("insomnia.profile")
local models = require("insomnia.models")
local scope = require("insomnia.scope")
local compact = require("insomnia.compact")
local model = models.catalog("codex-oauth/gpt-5.5")
return profile {
slug = "coder",
description = "Project coding profile",
model = model,
worker = {
reasoning = "high",
},
compaction = compact.ratio {
threshold = 0.8,
request = 0.9,
},
scope = scope.workspace_write(),
}
```
Profile が表現してよいのは reusable な recipe fields である。
- model selection / provider policy
- worker settings / reasoning
- compaction policy
- memory / web policy
- permissions / tool policy
- session diagnostics policy
- scope intent such as `scope.workspace_write()`
Profile に入れてはいけないもの:
- `pod.name`
- concrete `scope.allow` / `scope.deny`
- runtime directories, sockets, callback addresses
- active/pending/session/pod-store runtime state
- resolved absolute paths that bind the profile to one machine/workspace
- raw resolved secret material
これらが必要な場合は、resolver の runtime input または `--manifest` の explicit Manifest path で扱う。
## Profile selector semantics
`--profile` は以下を受け付ける。
| 形 | 意味 |
|---|---| |---|---|
| スカラー(`String`, `u32`, `bool` 等) | 上層に値があれば丸ごと置換 | | 省略 / `default` | registry default を使う。通常は bundled `builtin:default` |
| `Option<T>` | 上層が `Some` なら置換、`None` なら据え置き | | `<name>` | unqualified profile name。ambiguous なら fail closed |
| 配列スカラー(`worker.stop_sequences` 等) | 上層に値があれば配列ごと置換。追記マージはしない | | `builtin:<name>` / `user:<name>` / `project:<name>` | source-qualified selector |
| マップ(`tool_output.per_tool` 等) | キー単位でマージ、同一キーは上層優先 | | `path:<path>` / `./profile.lua` / `/abs/profile.lua` | 明示 path profile |
| `scope.allow` / `scope.deny` | **union**(各層から全部足す)。上位層は `deny` で下位層の `allow` を必ず削れる |
| `permissions.rule` | **union**(下位層の rule → 上位層の rule の順に評価)。`permissions.default_action` は上位層があれば上書き |
各層をマージした結果(`PodManifestConfig`)を `TryFrom<PodManifestConfig> `.nix` profile files are no longer supported. Reusable profiles are Lua; complete low-level recipes belong behind `--manifest`.
for PodManifest` が必須フィールド検証と絶対パス検証をかけて `PodManifest`
に変換する。
## パス解決 Discovery は bundled builtin profiles、user registry (`<config_dir>/profiles.toml`)、project registry (`<project>/.insomnia/profiles.toml`) を読む。後段の default が前段の default を上書きするため、project default は user/default builtin より優先される。unqualified ambiguous names は source-qualified suggestion を出して失敗する。
manifest 中のパス(`model.auth.file` / `scope.*.target` / Example `.insomnia/profiles.toml`:
`compaction.model.auth.file`)は相対記述を許容する。相対パスは
**各層のベース基準**で層ごとに絶対化され、そのあとで cascade merge に
かかる。層をまたいだ相対の意味ブレuser 層の `./keys` が project 層の
どこを指すのか曖昧)を避けるための設計。
| 層 | ベース | ```toml
default = "coder"
[profile]
coder = "profiles/coder.lua"
reviewer = "profiles/reviewer.lua"
[profile.orchestrator]
path = "profiles/orchestrator.lua"
description = "Project orchestrator"
```
Relative registry paths are resolved against the `profiles.toml` file that declares them.
## Controlled Lua environment
Profile evaluation runs with controlled host-provided `require`.
Host virtual modules:
- `require("insomnia")`
- `require("insomnia.profile")`
- `require("insomnia.models")`
- `require("insomnia.compact")`
- `require("insomnia.scope")`
Profile-local modules can be reused with dotted names such as `require("shared")` or `require("shared.models")`; they resolve only under the selected profile file's directory. Unsafe/unrestricted Lua facilities such as `os`, `io`, `debug`, unrestricted `package`, `dofile`, `loadfile`, `load`, and `collectgarbage` are unavailable by default.
## One-file Manifest mode
`--manifest <path>` reads exactly one TOML file, resolves relative paths against that file's parent directory, applies builtin defaults, and validates through `PodManifestConfig -> PodManifest`. It does not load user/project `manifest.toml` files and conflicts with `--profile`.
Use `--manifest` only when you need the complete low-level Manifest escape hatch or a focused debug fixture.
## Builtin defaults
Base defaults that are independent of profile choice live in Rust constants under `crates/manifest/src/defaults.rs` and in `PodManifestConfig::builtin_defaults()`. The bundled default role profile lives at `resources/profiles/default.lua` and is discovered as `builtin:default`.
デフォルト値を変更するときは、次のどちらを変更するのかを明確にする。
- all manifests/profiles の baseline default: Rust defaults
- ordinary dogfooding/default role: `resources/profiles/default.lua`
## Path resolution
Profile-local Lua module paths are resolved relative to the selected profile file's directory and are constrained to that directory tree.
Manifest paths in one-file Manifest mode are resolved relative to the manifest file's parent directory before validation. `scope.*.target` and auth file paths must be absolute by the time `PodManifest` is constructed; a remaining relative path indicates a resolver bug and returns `ResolveError::RelativePath`.
Pod cwd is not part of the Profile. It is the process `current_dir()` for manual startup, or the parent-selected `Command::current_dir` for spawned Pods.
## Unknown fields and type errors
Profile validation is stricter than old ambient manifest cascade semantics at the top-level boundary. Complete-Manifest/runtime-authority fields such as `manifest`, `config`, `pod`, and concrete `scope.allow`/`scope.deny` are diagnosed rather than silently treated as reusable profile data.
One-file Manifest deserialization keeps the Manifest compatibility behavior: unknown fields may warn/ignore where the Manifest schema allows it, while type mismatches are hard errors with file/path context.
## Prompt assets
`worker.instruction` refers to a prompt asset used as the main system prompt body. Import-map prefixes:
| Prefix | Resolution |
|---|---| |---|---|
| user manifest (`<config_dir>/manifest.toml`) | そのファイルの親ディレクトリ | | `$insomnia` | bundled `resources/prompts/` (`include_dir!`) |
| project manifest (`<project>/.insomnia/manifest.toml`) | **プロジェクトルート**`.insomnia/` の親)。`target = "."` がワークスペース全体を指すように | | `$user` | `<config_dir>/prompts/` |
| overlayinline TOML・programmatic | プロセスの `current_dir()` |
Pod の作業ディレクトリは manifest に含まれない。プロセス起動時の
`std::env::current_dir()` がそのまま Pod の pwd となるため、別の作業
ディレクトリで Pod を走らせたい場合は `cd` してから `insomnia-pod` を起動する
(または `SpawnPod` が子に対して行っているように、親プロセス側で
`Command::current_dir` を明示する)。
cascade merge 後の `TryFrom<PodManifestConfig>` では `ensure_absolute`
が不変条件チェックとしてだけ働く。相対パスが残っていれば上流の
resolve 段を取りこぼしている証拠なので `ResolveError::RelativePath`
返す。
## 未知フィールドと型エラー
- **未知フィールド**: `tracing::warn!` を出して無視。将来バージョンアップで読めない
旧設定が出るとユーザー体験が悪いため、`#[serde(deny_unknown_fields)]` は使わない。
- **型ミスマッチ**: `max_tokens = "100"` のような型エラーは hard error として
resolve 失敗させる。ファイルパスと位置情報をエラーメッセージに含める。
---
## manifest.toml 例
### ユーザー層(最小)
`<config_dir>/manifest.toml`:
```toml
[model]
ref = "anthropic/claude-sonnet-4-6"
auth = { kind = "api_key", file = "/home/you/.config/insomnia/keys/anthropic" }
```
`ref = "<provider>/<model_id>"` はプロバイダ / モデルカタログを引く短縮形。
`scheme` / `base_url` / `model_id` は provider 側の宣言から引かれ、`auth` も
カタログの `auth_hint` を起点に解決する。ここでは env 既定(`INSOMNIA_API_KEY_ANTHROPIC`
ではなく file から読みたいので `auth` だけ override している。詳細は
`crates/provider/README.md``resources/{providers,models}/builtin.toml` を参照。
### プロジェクト層(最小)
`<project>/.insomnia/manifest.toml`:
```toml
[[scope.allow]]
target = "/abs/path/to/project"
permission = "write"
[[scope.deny]]
target = "/abs/path/to/project/secrets"
permission = "read"
[compaction]
compact_threshold = 80000
```
### 全オプション例
```toml
[pod]
name = "reviewer"
# Form A: ref のみ(カタログから scheme / base_url / auth_hint / capability を全部引く)
# [model]
# ref = "anthropic/claude-sonnet-4-6"
#
# Form B: ref + 部分 overrideここで示している形
# カタログ起点に個別フィールドだけ上書き。ref 指定時は scheme / model_id / auth は任意 override。
#
# Form C: 完全 inlineカタログ無視。実験用 / カタログに無いモデル)
# [model]
# scheme = "anthropic"
# model_id = "claude-sonnet-4-6"
# auth = { kind = "api_key", file = "..." }
[model]
ref = "anthropic/claude-sonnet-4-6"
base_url = "https://api.anthropic.com"
auth = { kind = "api_key", file = "/home/you/.config/insomnia/keys/anthropic" }
[worker]
instruction = "$user/reviewer"
max_tokens = 4096
max_turns = 50
temperature = 0.3
top_p = 0.9
top_k = 40
stop_sequences = ["\n\n", "</stop>"]
reasoning = "medium" # 文字列 = effort label / 整数 = thinking budget tokens。詳細は docs/reasoning.md
[worker.tool_output]
default_max_bytes = 65536
[worker.tool_output.per_tool]
Read = 131072
Grep = 8192
[worker.file_upload]
max_bytes = 262144
[[scope.allow]]
target = "/abs/path/to/project"
permission = "write"
[[scope.allow]]
target = "/abs/path/to/docs"
permission = "read"
recursive = false
[[scope.deny]]
target = "/abs/path/to/project/secrets"
permission = "write"
[permissions]
default_action = "allow" # allow | deny | ask
[[permissions.rule]]
tool = "Bash"
pattern = "rm *"
action = "deny"
[[permissions.rule]]
tool = "Write"
pattern = "*.env"
action = "deny"
[web]
enabled = true
[web.search]
provider = "brave"
api_key_env = "BRAVE_SEARCH_API_KEY"
timeout_secs = 15
[web.fetch]
timeout_secs = 20
redirect_limit = 5
max_response_bytes = 2097152
max_output_bytes = 65536
[compaction]
prune_protected_tokens = 8000
prune_min_savings = 4096
compact_threshold = 80000
compact_request_threshold = 90000
compact_retained_tokens = 8000
compact_auto_read_budget = 8000
compact_worker_max_input_tokens = 50000
compact_worker_max_turns = 20
[compaction.model]
scheme = "gemini"
model_id = "gemini-2.0-flash"
auth = { kind = "api_key", file = "/home/you/.config/insomnia/keys/gemini" }
```
---
## `[worker]` 設定
`[worker]` は Pod 内の `llm_worker::RequestConfig` とターン制御へ渡す設定を持つ。
Provider ごとの wire 名の違いOpenAI の `max_completion_tokens` /
Responses の `max_output_tokens` / Gemini の `generation_config` など)は
scheme 側が吸収する。
| key | 型 | 既定 | 内容 |
|---|---|---|---|
| `instruction` | `String` | `$insomnia/default` | システムプロンプト本体として使う prompt asset 参照 |
| `max_tokens` | `u32` | 未指定 | 1 request の最大出力 token。scheme が provider の該当 wire field に投影。scheme ごとのセマンティクス差は `docs/reasoning.md` |
| `max_turns` | `NonZeroU32` | 未指定 | 1 run 内で Worker が進められる最大 turn 数 |
| `temperature` | `f32` | 未指定 | sampling temperature |
| `top_p` | `f32` | 未指定 | nucleus sampling |
| `top_k` | `u32` | 未指定 | top-k sampling。未対応 scheme では warning または provider 側挙動に任せる |
| `stop_sequences` | `Vec<String>` | `[]` | stop sequence。cascade では上層指定が配列ごと置換する |
| `reasoning` | `String` または `i32` | 未指定 | reasoning / thinking 制御。詳細は `docs/reasoning.md` |
| `tool_output.default_max_bytes` | `usize` | `65536` | tool result `content` の既定 byte cap |
| `tool_output.per_tool` | `Map<String, usize>` | `{}` | tool 名ごとの byte cap override |
| `file_upload.max_bytes` | `usize` | `262144` | submit 時の FileRef (`@<path>`) upload / attachment の byte cap |
生成設定は provider 別の値域検証を行わない。型が TOML と合わない場合は manifest
parse error になるが、provider が受け付けない値や組み合わせは API 応答で検出する。
## `[web]` 設定
`WebSearch` / `WebFetch` は通常の built-in function tool として登録されるが、manifest で明示的に有効化されるまでネットワークアクセスしない。無効または未設定の場合、tool call は「設定されていない」旨の明示的なエラーを返す。
```toml
[web]
enabled = true
[web.search]
provider = "brave"
api_key_env = "BRAVE_SEARCH_API_KEY" # API key は env 参照に置き、manifest に raw secret を書かない
timeout_secs = 15
[web.fetch]
timeout_secs = 20
redirect_limit = 5
max_response_bytes = 2097152
max_output_bytes = 65536
```
`WebSearch` の最初の provider は Brave Search API`https://api.search.brave.com/res/v1/web/search`)で、入力は `query` と任意の `limit` / `offset`。Brave の制約に合わせて `query` は 400 文字 / 50 words まで、`limit` は 1-20、`offset` は 0-9 に制限される。`timeout_secs` を省略した場合は安全な既定値が使われ、provider response は固定上限内で読み込まれる。
`WebFetch` は http/https URL のみを fetch し、timeout・redirect・response/output byte limit を適用する。localhost / private / link-local などの host/IP は fetch 前と各 redirect で拒否される。テストや明示的に信頼した環境では `[web] allow_private_addresses = true` または `[web.fetch] allow_private_addresses = true` を指定できる。
## `[permissions]` 設定
`[permissions]` が無い場合、ツール permission 層は無効で従来通り実行する。`[permissions]` を書く場合は `default_action = "allow" | "deny" | "ask"` が必須で、`[[permissions.rule]]` は宣言順に最初に一致した rule が採用される。一致しなければ `default_action` を使う。
```toml
[permissions]
default_action = "allow"
[[permissions.rule]]
tool = "Bash"
pattern = "rm *"
action = "deny"
```
`tool` は実行時に登録されているツール名(`Bash`, `Read`, `Write`, `Edit`, `Glob`, `Grep`, `WebSearch`, `WebFetch` 等)に対して大小文字を無視して照合する。`pattern` は built-in tool では主に `command` / `file_path` / `path` / `pattern` / `query` / `url` 引数に対する `*` / `?` ワイルドカードとして評価される。
`allow` は通常実行、`deny` はその tool call を実行せず `is_error = true` の synthetic tool result を履歴へ追加してターンを継続する。`ask` は型として受け付けるが、承認 protocol は未実装のため現在は headless に待機せず fail-closedsynthetic error resultになる。
## instruction とプロンプト資産
### `worker.instruction` フィールド
Pod のシステムプロンプトの**本体**として使うプロンプト資産への参照。
import-map 形式のプレフィックスで指定する:
| プレフィックス | 解決先 |
|---|---|
| `$insomnia` | バイナリ同梱の `resources/prompts/``include_dir!` |
| `$user` | `<config_dir>/prompts/``manifest::paths` で解決) |
| `$workspace` | `<project>/.insomnia/prompts/` | | `$workspace` | `<project>/.insomnia/prompts/` |
- `.md` 拡張子は省略する(例: `$insomnia/default``resources/prompts/default.md` `.md` extension can be omitted, e.g. `$insomnia/default` resolves to `resources/prompts/default.md`. Missing files are hard errors; prefixes do not fall through.
- 省略時のデフォルト値は `$insomnia/default``defaults::DEFAULT_INSTRUCTION`
- 指定した prefix の dir に該当ファイルが無ければ **hard error**fallthrough しない)
### ビルトインプロンプト Profile and one-file Manifest CLI paths currently use builtin prompt assets only for initial loader construction. `$insomnia/...` works; `$user/...` and `$workspace/...` prompt refs need a future explicit prompt-loader source design instead of reviving ambient manifest discovery.
`resources/prompts/` 以下に同梱: The rendered instruction body is followed by fixed Rust-provided sections for working boundaries and, when present, `AGENTS.md`. User templates cannot remove the scope section.
| 名前 | 用途 |
|---|---|
| `default` | デフォルトの instruction 本体。workspace / tool-usage をインクルード |
| `common/workspace` | cwd・日付の注入 |
| `common/tool-usage` | ツール使用の共通ガイダンス |
### `{% include %}` の相対解決
テンプレート内で `{% include "name" %}` のようにプレフィックス無しで書いた場合、
**include を書いたファイル自身のプレフィックスとディレクトリ**からの相対で解決する:
- `$insomnia/default.md` 内の `{% include "common/workspace" %}``$insomnia/common/workspace`
- `$user/custom.md` 内の `{% include "$insomnia/common/tool-usage" %}` → 明示的プレフィックスが優先
### システムプロンプトの最終構造
`instruction` テンプレートのレンダリング結果に、Rust 側で以下の**固定セクション**を付加する。
ユーザーテンプレートからは触れない領域:
```
<instruction のレンダ結果>
---
## Working boundaries
<scope.summary()>
--- ← AGENTS.md が不在なら省略
## Project instructions (AGENTS.md)
<AGENTS.md 本文> ← AGENTS.md が不在なら省略
```
- scope セクションは**必ず**出力される
- AGENTS.md セクションは不在時に区切り `---` ごと省略
---
## `insomnia-pod` CLI ## `insomnia-pod` CLI
`insomnia-pod` の通常起動は profile discovery/default から runtime manifest を作る。user/project `manifest.toml` の ambient cascade は通常起動では使わない。 Normal fresh startup uses profile discovery/default selection:
``` ```text
insomnia-pod [--profile <selector>] [--profile-pod-name <name>] [-s/--store <path>] [--session <uuid>] insomnia-pod [--profile <selector>] [--profile-pod-name <name>] [-s/--store <path>]
``` ```
| フラグ | 説明 | | Flag | Description |
|---|---| |---|---|
| `--profile <selector>` | builtin/user/project profile registry から Lua profile を選択。省略時は registry default通常は `builtin:default` | | `--profile <selector>` | Select a Lua profile. Omitted means registry default, normally `builtin:default` |
| `--profile-pod-name <name>` | profile resolution 時に fresh spawn 用の runtime `pod.name` を指定 | | `--profile-pod-name <name>` | Fresh-spawn runtime `pod.name` override for profile resolution |
| `-s, --store <path>` | セッション永続化ディレクトリ(デフォルト: `<data_dir>/sessions/`、`manifest::paths` で解決) | | `-s, --store <path>` | Session persistence directory, default `<data_dir>/sessions/` |
| `--session <uuid>` | 既存 session id から Pod を復元し、同じ jsonl に後続 turn を追記する |
単一ファイルだけで起動したい場合は `--manifest` を指定する。 Restore/attach uses Pod/session state and does not re-evaluate profile sources.
``` ```text
insomnia-pod --manifest <path> [-s/--store <path>] [--session <uuid>] insomnia-pod --pod <name>
insomnia-pod --session <uuid>
``` ```
`--manifest` は指定 TOML 1 枚だけを読み、builtin defaults を merge したうえで `PodManifestConfig -> PodManifest` の required validation を通す。user / project manifest layer は読まない。`--profile`、`--project` とは併用不可。 Spawn children use hidden `--spawn-config-json`, `--adopt`, and `--callback <path>` flags. These are internal handoff details used by `SpawnPod` after the parent has allocated scope and prepared the child config.
spawn 子 Pod 用の内部フラグとして `--adopt``--callback <path>` がある。これらは `SpawnPod` が scope allocation と親 callback socket を引き継がせるために使うもので、通常の手動起動では使わない。 ## Programmatic boundary
Pod の作業ディレクトリは `insomnia-pod` 起動時の cwd が直接使われる。別ディレクトリで New code should resolve profiles through the profile resolver and then construct Pods from the resulting `PodManifest` and `PromptLoader`. One-file Manifest helpers remain for tests/debugging. Avoid reintroducing user/project manifest cascade APIs as normal startup behavior.
動かしたい場合は `cd <path> && insomnia-pod ...` のように外側で `cd` してから起動する。
引数無しで起動すると、profile registry default通常は bundled `builtin:default`)で起動する。
---
## プログラマティック API
```rust ```rust
use pod::{Pod, PodFactory}; let (manifest, loader) = resolve_profile_or_manifest(cli_inputs)?;
let (manifest, loader) = PodFactory::new()
.with_user_manifest_auto()? // manifest::paths から自動読み込み、不在 OK
.with_project_manifest_auto()? // cwd から上方向に .insomnia/ を探索、不在 OK
.with_overlay_toml(overlay)? // programmatic な最上層 overlay
.resolve()?; // -> (PodManifest, PromptLoader)
let pod = Pod::from_manifest(manifest, store, loader).await?; let pod = Pod::from_manifest(manifest, store, loader).await?;
``` ```
`Pod::from_manifest_toml(toml, store)` は単層 manifest を TOML 文字列で直接投げる
便利関数テスト・デバッグ向け。builtins-only のプロンプトローダで動く。

View File

@ -1,67 +0,0 @@
# Insomnia Nix profile helpers.
#
# A profile file can use:
#
# let insomnia = import ./path/to/profile-lib.nix {};
# in insomnia.mkProfile {
# name = "coder";
# manifest = insomnia.mkManifest { ... };
# }
#
# The output is consumed by `insomnia-pod --profile <path>` via
# `nix eval --json --file <path>`.
{ }:
let
profileFormat = "insomnia.nix-profile.v1";
optional = name: value:
if value == null then {} else { ${name} = value; };
secretRef = ref: {
kind = "secret_ref";
inherit ref;
};
mkManifest = manifest: manifest;
mkProfile =
{ name ? null
, description ? null
, manifest ? null
, config ? null
, ...
}@args:
let
resolvedManifest =
if manifest != null then manifest
else if config != null then config
else removeAttrs args [ "name" "description" "manifest" "config" ];
in
{
profile = ({ format = profileFormat; }
// optional "name" name
// optional "description" description);
manifest = resolvedManifest;
};
semanticPresets = {
# Skeleton for users to extend in their own Nix. Rust does not attach any
# hidden semantic meaning to these helpers; they only generate manifest JSON.
codingAssistant = { modelId ? "claude-sonnet-4-20250514", authRef ? null }:
{
model = {
scheme = "anthropic";
model_id = modelId;
} // (if authRef == null then {} else { auth = secretRef authRef; });
};
};
in
{
inherit profileFormat mkProfile mkManifest semanticPresets;
secrets = {
ref = secretRef;
};
}

View File

@ -1,40 +0,0 @@
let
insomnia = import ../profile-lib.nix {};
in
insomnia.mkProfile {
name = "default";
description = "Bundled default Insomnia coding profile";
manifest = insomnia.mkManifest {
pod.name = "insomnia";
scope.allow = [
{ target = "."; permission = "write"; recursive = true; }
];
session.record_event_trace = true;
worker.reasoning = "high";
model.ref = "codex-oauth/gpt-5.5";
compaction = {
threshold = 200000;
request_threshold = 240000;
worker_context_max_tokens = 100000;
};
memory = {
extract_threshold = 50000;
consolidation_threshold_files = 5;
consolidation_threshold_bytes = 50000;
};
web = {
enabled = true;
search = {
provider = "brave";
api_key_env = "BRAVE_SEARCH_API_KEY";
};
};
};
}