This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# AskUser
|
||||
|
||||
ユーザーに選択肢つきで質問し、回答があるまでタスクを停車する park ツール。
|
||||
|
||||
## いつ使うか
|
||||
|
||||
- 指示が曖昧で、**進め方の分岐を自分で列挙できる**とき(デプロイ先、対象範囲、形式の選択など)
|
||||
- Mission Brief と今回の指示がずれて見え、どちらに従うべきか確認したいとき
|
||||
- 複数の論点をまとめて確認したいとき(1 回の停車で最大 4 問)
|
||||
|
||||
## いつ使わないか
|
||||
|
||||
- 選択肢を作れない開放的な質問(「何をお望みですか?」)→ `complete({status:"needs_user_input", missing_info})`
|
||||
- 自明な既定値があり、進めてから報告すれば足りる場合 → 質問せず進める
|
||||
- subtask 実行 → 利用不可(degrade メッセージが返る)。自明な選択肢で進めるか complete で報告
|
||||
|
||||
## 書き方
|
||||
|
||||
```json
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
"question": "デプロイ先はどちらにしますか?",
|
||||
"header": "デプロイ先",
|
||||
"options": [
|
||||
{ "label": "本番", "description": "即時反映。ロールバック可" },
|
||||
{ "label": "ステージング", "description": "検証後に手動昇格" }
|
||||
],
|
||||
"multiSelect": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- 選択肢は **2〜6 個・相互排他**に。網羅しようとせず、迷う候補は入れずに自由記述(UI が常に追加する「その他」)に任せる
|
||||
- `question` は文脈を含め単体で意味が通るように。ユーザーは会話全体を読み直さない
|
||||
- `header` はカードの短いラベル。省略可
|
||||
|
||||
## 挙動
|
||||
|
||||
- 呼ぶとタスクは停車(`waiting_human`)し、ワーカーは解放される。ツール内で回答を待たない
|
||||
- 回答は次の再開時に**ユーザーメッセージ**として届く(「見出し: 選択」の平文)
|
||||
- ASK 上限は停車 1 回で 1 消費(質問数ではない)。上限は askPolicy 設定に従う
|
||||
- 回答を受けたら、決定事項を `MissionUpdate` の `decisions` / `clarifications` に記録すること
|
||||
+4
-2
@@ -58,7 +58,7 @@ Bash 実行の隔離方式を選ぶ。**2 つの設定は直交する**:
|
||||
|
||||
| `bash_sandbox` | 挙動 |
|
||||
|----------------|------|
|
||||
| `auto`(既定) | bwrap があれば bwrap サンドボックス、無ければ hardened(whitelist + パススコープ + env スクラブ)にフォールバック |
|
||||
| `auto`(既定) | bwrap があれば bwrap サンドボックス、無ければ hardened(whitelist + パススコープ + env スクラブ)にフォールバック。ただし外向き通信の遮断(`bash_allow_network: false`、既定)を選んだままフォールバックする組み合わせは**起動失敗**——hardened には遮断する仕組みが無いため (#1235) |
|
||||
| `always` | bwrap を強制。bwrap 不在なら**起動失敗**(本番推奨) |
|
||||
| `off` | bwrap を使わず exec(**env スクラブと、unrestricted でなければ whitelist + パススコープは維持**)。デバッグ用・非推奨 |
|
||||
|
||||
@@ -84,4 +84,6 @@ Bash 実行の隔離方式を選ぶ。**2 つの設定は直交する**:
|
||||
|
||||
- コンテナ/ホストで **user namespace** が有効であること (PVE: `features: nesting=1`)
|
||||
- `bwrap` バイナリがインストール済みであること
|
||||
- `bash_sandbox: always`(または `bash_unrestricted: true`)では起動時に bwrap の動作確認を行い、失敗時はエラー終了する。`auto` では失敗時に警告ログを出し hardened へフォールバック
|
||||
- `bash_sandbox: always`(または `bash_unrestricted: true`)では起動時に bwrap の動作確認を行い、失敗時はエラー終了する。`auto` は失敗時の挙動が `bash_allow_network` で変わる (#1235):
|
||||
- `false`(既定・遮断する) → **エラー終了**。設定は「遮断する」なのに hardened では遮断されない、という食い違いを抱えたまま走らせない
|
||||
- `true`(遮断しない) → 警告ログを出して hardened へフォールバック
|
||||
|
||||
+69
-6
@@ -304,15 +304,78 @@ BrowseWithSession({
|
||||
|
||||
ダウンロードを認証付きで行いたい場合は、Browser Sessions 機能で対象サイトのログインセッションを保存し、タスクで bind した状態で BrowseWeb を呼ぶこと。
|
||||
|
||||
## ソースライブラリへの蓄積(スペースタスク)
|
||||
## ページの魚拓(本文+画像)
|
||||
|
||||
スペースのタスクで BrowseWeb がページ本文(スナップショット / セレクタ抽出 / dumpHtml 等の抽出テキスト)を取得すると、その本文が Markdown として `source/` に保存され、出典メタ(URL・取得日時・サイズ・`tool: "BrowseWeb"`)が `source/index.jsonl` に追記される。WebFetch / DownloadFile と同じソースライブラリに合流するので、「そのスペースのソース(資料)」が自動で溜まり、後から追加調査やグラウンディングに使える。
|
||||
BrowseWeb が開いたページは、本文とそのページに載っていた画像が `source/` に自動で溜まる。出典メタ(URL・取得日時・サイズ・`tool: "BrowseWeb"`)は `source/index.jsonl` に 1 行ずつ追記される。WebFetch / DownloadFile と同じソースライブラリに合流するので、後から追加調査やグラウンディングに使える。
|
||||
|
||||
- 同一 URL は重複保存しない(既に index にあればスキップ)。同じ URL を 2 回閲覧しても 1 件のまま
|
||||
- スペース文脈でないタスク(ephemeral / gitea issue 等)では従来どおり蓄積しない(`ctx.spaceId` が無いとき)
|
||||
**属している場所で挙動は変わらない。**スペースのタスクでも、タスク一覧から直接作ったタスクでも、定期実行・サブタスクでも同じように溜まる(issue #1097 / 由来 #1093)。
|
||||
|
||||
### 何が・どこに
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| ページ本文 | `source/<ページ名>.md` |
|
||||
| ページに載っていた画像 | `source/images/<名前>.<拡張子>` |
|
||||
| 索引 | `source/index.jsonl`(1 行 1 件) |
|
||||
|
||||
索引の画像行は `kind: "image"` を持ち、`page_url` / `page_file` で**どのページに載っていた画像か**を指す。`sha256`(中身の指紋)・`width` / `height` も入る。ページ本文の行は従来どおり `kind` を持たない。
|
||||
|
||||
保存された画像は `ReadImage({ file_path: "source/images/xxx.png" })` でそのまま読める。**画像のための新しいツールは無い。**
|
||||
|
||||
### 取り方
|
||||
|
||||
画像は **開いているページの文脈**で取りに行く(`page.request`。Cookie・User-Agent はブラウザコンテキストのもの、Referer は開いている URL)。
|
||||
|
||||
**画像の URL を別のツール(DownloadFile 等)に渡して取り直してはいけない。**ログインが要るページ・リンク元を見て弾くサイト・期限つきの署名が付いた URL がいずれも取れなくなり、この機能の価値が消える。ここがスクリーンショット依存を外せる理由。
|
||||
|
||||
候補は 2 つの経路から並べる。
|
||||
|
||||
1. `document.images` の `currentSrc`(srcset / picture で実際に選ばれた 1 枚)。実寸(`naturalWidth` / `naturalHeight`)が取れるので寸法の下限が効く
|
||||
2. Resource Timing(`performance.getEntriesByType('resource')`)のうち `initiatorType` が `css` / `img` のもの。**スタイルシートの背景として敷かれた画像はここにしか出ない**——記事の主役の写真が背景で敷かれている作りのサイトでは、`<img>` だけ見ていると図が丸ごと落ちる。実寸は取れないので、下限の判定は見送って残す側に倒す
|
||||
|
||||
並び順は `<img>` が先。取りに行く回数には上限があるので、フォントや装飾で枠を食い潰させない。資源の一覧には画像でないもの(フォント等)も混じるが、**種類で選り分けない**——画像かどうかは取ってきた中身の先頭バイトで決める。
|
||||
|
||||
`data:` URL はその場で復号する。`blob:` は取りに行けないので対象外。インラインの `<svg>` 要素と `<canvas>` は、そもそも取りに行く URL が無いので入らない。
|
||||
|
||||
`page.request` はページの route 傍受を通らないので、**取得前に SSRF の関所(`ssrfCheck`)を通す**。ページに `<img src="http://169.254.169.254/...">` を仕込まれても内部へ取りに行かせない。許可ホスト(`tools.webfetch_allowed_hosts`)は BrowseWeb 本体と同じものを使う。
|
||||
|
||||
### 歯止め(意味は見ない)
|
||||
|
||||
「これは飾りだから捨てる」という判定は書かない。書けば必ず漏れるし、漏れて困るのは「あとから欲しかったのに無い」側。歯止めは意味を見ないものだけ。
|
||||
|
||||
| 歯止め | 既定 | config.yaml(`tools` 配下) |
|
||||
|---|---|---|
|
||||
| 中身の指紋で畳む | 常時 | (設定なし) |
|
||||
| 1 ページあたりの枚数 | 20 | `browse_image_max_per_page` |
|
||||
| 1 枚あたりの容量 | 5MB | `browse_image_max_size_mb` |
|
||||
| 容量の下限 | 1024 バイト | `browse_image_min_bytes` |
|
||||
| 寸法の下限 | 32 ピクセル | `browse_image_min_dimension` |
|
||||
| 取りに行く回数 | 枚数の上限 × 3(既定 60 回) | (設定なし。枚数の上限に連動) |
|
||||
|
||||
寸法の下限は **実寸が取れたときだけ**効く。取れていない画像は残す側に倒す(飾りは入る前提)。
|
||||
|
||||
**回数の上限は「残した枚数」とは別に数える。**残した枚数だけを見ていると、1 枚も残らないページ(配信元が落ちている・下限を下回る画像ばかり)で候補の全件に取りに行ってしまう。1 枚あたり 15 秒待つので、候補が数百あるページを開いただけでブラウザ操作が何分も返らなくなる。
|
||||
|
||||
溜まったものを自動で削除する仕組みは入れない。索引に参照が残るので、「どのページからも参照が無い」「1 ページでしか使われず小さい」といった条件で後から掃除する道は残る。
|
||||
|
||||
### その他
|
||||
|
||||
- 同一 URL のページ本文は重複保存しない(既に index にあればスキップ)。**ただし本文が既出でも画像は取りに行く**——WebFetch で本文を読んでから、図が要ると気づいてブラウザで開き直す流れを塞がないため。再訪の抑止は 3 段。①このプロセスでこのページの収集を試したか(`hasPageArchiveAttempt`。索引に痕跡が残らない「1 枚も溜まらなかったページ」がここに当たる。中断で切ったときは記憶しない)、②索引にこのページの画像行があるか、③すり抜けた分は指紋の判定が止める
|
||||
- 中身が同じ画像は実ファイルを 1 枚に畳み、索引の行だけ足す(ロゴ・アイコンが何ページぶんも積み上がらない)
|
||||
- 空・エラー結果、`http(s)` 以外(`file://` 等)の URL はソース化しない
|
||||
- スクリーンショットはソースではないので `output/` に残り、`source/` には入らない(ソースはあくまでテキスト)
|
||||
- これは純粋な追記サイドライトで、BrowseWeb の戻り値・スクショ・`logs/` 出力は一切変わらない
|
||||
- スクリーンショットはソースではないので `output/` に残り、`source/` には入らない
|
||||
- 魚拓は副作用。失敗してもブラウザ操作そのものは通る。戻り値には `[archive] source/images/ に 7 枚(索引: source/index.jsonl)` の形で 1 行だけ足る。落ちた枚数は理由ごとに分けて出す(`上限・下限で対象外` / `画像として読めない中身` / `取得できず`)
|
||||
- 本文を取らない操作列(`goto` → `screenshot` 等)でも `goto` 直後に魚拓する。`BrowseWithSession` も同じ経路を通る
|
||||
|
||||
### 形式の判別
|
||||
|
||||
拡張子と `content-type` は**見ない**。どちらも当てにならないため——オブジェクトストレージの署名つき URL は拡張子を持たず `application/octet-stream` で配られることが多く、逆に `content-type: image/png` でも中身が HTML のエラーページということがある。
|
||||
|
||||
判別は**中身の先頭バイト**で行う(`sniffImageExtension`)。PNG / JPEG / GIF / WebP / BMP / TIFF / ICO / AVIF / SVG を見分け、**どれにも当たらないものだけ捨てる**。形式の判別であって、意味の判定ではない。AVIF は主ブランドが `mif1` で `avif` が互換ブランド側に載る配り方があるので、`ftyp` ボックスの互換ブランドの並びまで見る。
|
||||
|
||||
保存した画像は `ReadImage` がそのまま読める。SVG / AVIF / TIFF / ICO はモデルが直接受け取れないので、`ReadImage` 側が PNG に起こしてから渡す。唯一の例外は **BMP を格納した古い形式の `.ico`**(sharp が読めない)で、この場合は理由付きのエラーが返る。溜まってはいるので、必要なら別途変換できる。
|
||||
|
||||
動画・音声は対象外(`<img>` にも Resource Timing の `img` / `css` にも出ない)。フォントは候補には入りうるが、先頭バイトで画像でないと分かるので保存されない。
|
||||
|
||||
## SSRF 保護
|
||||
|
||||
|
||||
+34
-1
@@ -1,4 +1,4 @@
|
||||
# カレンダーツール(AddCalendarEvent / ListCalendarEvents)
|
||||
# カレンダーツール(AddCalendarEvent / ListCalendarEvents / DeleteCalendarEvent)
|
||||
|
||||
スペース別カレンダーに「予定」を読み書きするツール。予定は受動的なエントリで、
|
||||
cron の定期タスク(`scheduled_tasks`)とは別概念(予定は実行しない)。
|
||||
@@ -39,9 +39,42 @@ AddCalendarEvent({ title: "現地出張", date: "2026-07-10", end_date: "2026-07
|
||||
|
||||
範囲を省略すると全期間。結果は日付→時刻順(終日は各日の先頭)で返る。
|
||||
|
||||
## DeleteCalendarEvent
|
||||
|
||||
スペースの予定を id で 1 件削除する。
|
||||
|
||||
引数:
|
||||
- `id`(必須): `ListCalendarEvents` が返す予定の id(正の整数)
|
||||
|
||||
**削除できるのは `created_by` が `agent` の予定だけ。**利用者が画面から入れた予定
|
||||
(`created_by` = `user`。この列が無かった頃の古い行も `user` として扱われる)は、
|
||||
id を渡されても削除せず `isError: true` を返す。
|
||||
|
||||
```
|
||||
ListCalendarEvents({ from: "2026-07-01", to: "2026-07-31" })
|
||||
// - 2026-07-01 10:00 「定例MTG」(id=12, agent)
|
||||
// - 2026-07-03 (終日)「健康診断」(id=13, user)
|
||||
DeleteCalendarEvent({ id: 12 }) // → 削除される
|
||||
DeleteCalendarEvent({ id: 13 }) // → isError。利用者が作成した予定
|
||||
```
|
||||
|
||||
返り値(`isError: true` のもの):
|
||||
|
||||
| 渡した id | 返る内容 |
|
||||
|---|---|
|
||||
| 利用者が作成した予定 | この予定は利用者が作成したため削除できません。削除できるのはエージェントが作成した予定(created_by=agent)だけです |
|
||||
| 別スペースの予定 | 指定された id の予定はこのスペースに見つかりません |
|
||||
| 存在しない・すでに削除済み | 指定された id の予定はこのスペースに見つかりません |
|
||||
|
||||
別スペースの id と存在しない id で**文面を同一にしている**のは意図的。文面が違うと、
|
||||
その id の予定が他のスペースにあることを差分で漏らす。
|
||||
|
||||
## gotcha
|
||||
|
||||
- `date` / `from` / `to` は `YYYY-MM-DD`、`time` / `end_time` は `HH:MM` 厳守。形式違反はエラー。
|
||||
- `end_time` は `time` なしには指定できない(終日の予定に終了時刻は持てない)。
|
||||
- スペース未所属のタスクからは呼べない(`ctx.spaceId` 必須)。
|
||||
- 予定はスペースの可視性に従う。イベント単独の可視性は持たない。
|
||||
- `DeleteCalendarEvent` は利用者が作成した予定を消せない。一覧の行末に出る `agent` / `user` が判定に使われる値そのもの。
|
||||
- 予定に紐づくリマインダーは予定と一緒に消える(予定の行に載っているため)。画面からの削除と同じ挙動。
|
||||
- 予定の書き換えはツールとして用意していない(削除のみ)。
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
サブエージェントを同期的にインライン実行して、重い処理を委譲し本体のコンテキストを節約する。サブエージェントの中間ターンは親に見えないため、複雑な作業の最終結果だけを取得できる。
|
||||
|
||||
**タスク分解の既定手段**。research / general / brainstorming など調査・分析系のピースは、重いサブ調査を Delegate で **1 件ずつ直列に** こなす設計になっている。並列の別ジョブ(SpawnSubTask)はデフォルト無効で、本当に並列が必要なワークスペースだけが **設定 → ツール** タブで明示的に有効化する。ほとんどのタスクは Delegate で分解が完結するため、まず Delegate を検討する。
|
||||
**タスク分解の既定手段**。research / general など調査・分析系のピースは、重いサブ調査を Delegate で **1 件ずつ直列に** こなす設計になっている。並列の別ジョブ(SpawnSubTask)はデフォルト無効で、本当に並列が必要なワークスペースだけが **設定 → ツール** タブで明示的に有効化する。ほとんどのタスクは Delegate で分解が完結するため、まず Delegate を検討する。
|
||||
|
||||
## 基本
|
||||
|
||||
|
||||
@@ -28,18 +28,33 @@ If there is no record (a file that pre-dates the ledger), you get an
|
||||
`unknown`-equivalent message — treat the file as possibly belonging to another
|
||||
task and verify its contents before overwriting.
|
||||
|
||||
## ListWorkspaceFiles({ path?, sourceKind?, createdByTaskId?, lastModifiedByTaskId?, includeUnknown?, limit? })
|
||||
## ListWorkspaceFiles({ path?, sourceKind?, createdByTaskId?, lastModifiedByTaskId?, since?, until?, includeUnknown?, limit? })
|
||||
|
||||
Returns a bounded listing (default 50, max 200 rows) of known files with a
|
||||
one-line provenance summary each. It never returns file contents.
|
||||
one-line provenance summary each. It never returns file contents. Each line
|
||||
starts with the file's **date**.
|
||||
|
||||
Filters:
|
||||
|
||||
- `path` — path prefix, e.g. `output/`.
|
||||
- `sourceKind` — filter by source kind.
|
||||
- `createdByTaskId` / `lastModifiedByTaskId` — filter by task.
|
||||
- `since` / `until` — date range, inclusive on both ends. Accepts `YYYY-MM-DD`
|
||||
or a full ISO timestamp; a bare `until` date covers that whole day.
|
||||
- `includeUnknown` — set `false` to hide `unknown` / `imported_existing` files.
|
||||
|
||||
### The date
|
||||
|
||||
A file's date is `last_modified_at`, falling back to `first_seen_at`. Display,
|
||||
ordering and `since` / `until` all use that same one date, so a date you read
|
||||
off a listing can be pasted straight back into a range filter.
|
||||
|
||||
### Selecting only this task's files
|
||||
|
||||
The job's context block carries `このタスクの番号: <id>`. Pass it as
|
||||
`createdByTaskId` to see only what this task made, or use it to tell your own
|
||||
files apart from ones that were already in the workspace.
|
||||
|
||||
## Guidance
|
||||
|
||||
- Do NOT blindly edit files whose `source_kind` is `user_input` or whose
|
||||
|
||||
@@ -6,25 +6,35 @@ MAESTRO のプロジェクト内ドキュメントを **カテゴリ別に一覧
|
||||
|
||||
なし。
|
||||
|
||||
## 出力(3 カテゴリの Markdown)
|
||||
## 出力(4 カテゴリの Markdown)
|
||||
|
||||
| セクション | 内容 | 読み込み形式 |
|
||||
|-----------|------|------------|
|
||||
| 利用者向けヘルプ | `ui/src/content/help/*.md` の frontmatter `title` | `help/<name>` |
|
||||
| Piece 一覧 | `pieces/*.yaml` の名前+ description 1 行 | `piece/<name>` |
|
||||
| ドキュメント | `docs/` 配下の `.md`(後述の除外を除く) | `docs/<path>` |
|
||||
| ツール参照 | `docs/tools/*.md` 全件 | `tool/<name>`(`ReadToolDoc` と同等) |
|
||||
|
||||
実際の出力から抜粋(各行は 1 件ずつ、見出しの直後に空行が入る):
|
||||
|
||||
```
|
||||
ListAppDocs()
|
||||
→
|
||||
# 利用者向けヘルプ (`help/<name>` で読み込み — アプリ内ヘルプと同じ内容)
|
||||
|
||||
- `help/06-schedules` — 自動実行を設定する
|
||||
...
|
||||
# Piece 一覧 (`piece/<name>` で読み込み)
|
||||
- `piece/chat` — 雑多な依頼に答える汎用ピース
|
||||
|
||||
- `piece/chat` — 汎用デフォルト piece。質問・調査・コード生成・文書作成・データ処理など、
|
||||
...
|
||||
# ドキュメント (`docs/<path>` で読み込み)
|
||||
- `docs/mcp` — MCP 連携の概要
|
||||
|
||||
- `docs/mcp` — The orchestrator can call tools hosted on external **MCP servers** (OAuth-secured
|
||||
...
|
||||
# ツール参照 (`tool/<name>` で読み込み — ReadToolDoc と同等)
|
||||
- `tool/browseweb` — ヘッドレスブラウザで…
|
||||
|
||||
- `tool/browseweb` — ヘッドレスブラウザで Web ページを操作するツール。同一ジョブ内ではブラウザコンテキスト(Cookie・ログイン状態)が永続化される。
|
||||
```
|
||||
|
||||
## 一覧から除外されるもの
|
||||
@@ -38,7 +48,7 @@ ListAppDocs()
|
||||
|
||||
## 制約
|
||||
|
||||
- description は各ファイルの frontmatter を飛ばした最初の見出し/段落から最大 140 字を自動抽出。
|
||||
- description は各ファイルの frontmatter を飛ばした最初の見出し/段落から最大 140 字を自動抽出。利用者向けヘルプだけは frontmatter の `title` を使う(無ければファイル名)。
|
||||
- 合計エントリが 150 件を超えると、末尾に「個別取得を促す注記」が付く。
|
||||
|
||||
## 関連
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# ListFileStoreLocations / ListFileStoreFiles
|
||||
|
||||
利用者が SharePoint / OneDrive / Box のうち「ここだけ見てよい」と決めて登録した
|
||||
フォルダを、エージェント側から一覧するための 2 つの道具。
|
||||
|
||||
**中身は取得できない。** 取れるのは、そのフォルダの直下にある項目の
|
||||
名前・フォルダかどうか・大きさ・更新日時だけ。ファイルを開いて読むのは別の段で、
|
||||
この道具にはそのための引数も戻り値もない。
|
||||
|
||||
## 使う順番
|
||||
|
||||
1. `ListFileStoreLocations()` — 登録済みの場所を一覧する。引数なし。
|
||||
2. 出てきた `id` を `ListFileStoreFiles({ locationId })` に渡して、その直下を見る。
|
||||
|
||||
`id` は 1 の一覧から得たものだけが使える。当てずっぽうの id は「見つかりません」に
|
||||
なる(他人の場所を当てても同じ答えなので、存在の有無は分からない)。
|
||||
|
||||
## 例
|
||||
|
||||
```
|
||||
ListFileStoreLocations()
|
||||
→ ## 外部ファイル保存の場所 — 2 件
|
||||
- id: 7f3c…
|
||||
service: SharePoint / OneDrive
|
||||
path: /Shared Documents/請求書
|
||||
library: Shared Documents
|
||||
site: https://contoso.sharepoint.com/sites/sales
|
||||
connection: 営業部 SharePoint
|
||||
- id: 91ab…
|
||||
service: Box
|
||||
path: /経理/請求書
|
||||
site: https://app.box.com/folder/123456789
|
||||
connection: 経理部 Box
|
||||
|
||||
ListFileStoreFiles({ locationId: "7f3c…" })
|
||||
→ ## /Shared Documents/請求書(Shared Documents)— 3 件
|
||||
- 2026-07.xlsx size=20.0 KB updated=2026-07-31T09:00:00Z
|
||||
- 2026-06.xlsx size=19.2 KB updated=2026-06-30T09:00:00Z
|
||||
- [フォルダ] 控え updated=2026-05-02T04:11:00Z
|
||||
```
|
||||
|
||||
`service` は保存先の種類。Box にはライブラリ(文書ライブラリ)に当たる階層が
|
||||
無いので `library` の行が出ない。`site` の行は、SharePoint ならサイトの URL、
|
||||
Box ならそのフォルダのページの URL になる。
|
||||
|
||||
## 制限
|
||||
|
||||
| 事項 | 実際の動き |
|
||||
|------|-----------|
|
||||
| 深さ | 登録された 1 フォルダの**直下のみ**。中のフォルダには入れない |
|
||||
| 場所の指定 | 登録時に決まっている。SharePoint はサイトとライブラリとフォルダ、Box はフォルダ番号ひとつ |
|
||||
| 中身 | 取得できない。ダウンロードも本文の抽出もできない |
|
||||
| 件数 | 既定 500 件。超えた場合は「これで全部ではありません」と出力に付く |
|
||||
| 誰の資格情報か | **タスクの持ち主**のもの。共有されたタスクを別の人が動かしても、使われるのは持ち主の接続 |
|
||||
| 書き込み | できない。要求している権限は読み取りのみ |
|
||||
|
||||
## 出ないとき
|
||||
|
||||
出力の文面がそのまま次の一手になる。
|
||||
|
||||
| 出力の趣旨 | 状況 | できること |
|
||||
|-----------|------|-----------|
|
||||
| この実行では使えません | 機能が有効になっていない実行 | 手元のワークスペースのファイルで進める |
|
||||
| 登録されている保存場所はありません | 接続はあるがフォルダ未登録 | 利用者に登録を頼む |
|
||||
| 見つかりません | id が違う・他人の場所 | `ListFileStoreLocations` を取り直す |
|
||||
| 接続がまだ済んでいません | 持ち主が未接続 | 利用者に設定画面からの接続を頼む |
|
||||
| 認可の期限が切れています | 更新できなくなった | 利用者に接続し直しを頼む |
|
||||
|
||||
管理者がワークスペースの設定でツール分類 `filestore` を切っている場合、この 2 つは
|
||||
そもそもツール一覧に出ない。
|
||||
+333
-9
@@ -1,4 +1,4 @@
|
||||
# Office ファイル系ツール(ReadPdf / ReadExcel / ReadDocx / ReadPPTX / ReadMsg / PdfToImages / SplitExcelSheets / SplitDocxSections)
|
||||
# Office ファイル系ツール(ReadPdf / ReadExcel / ReadDocx / ReadPPTX / ReadMsg / PdfToImages / SplitExcelSheets / SplitDocxSections / EditExcel / EditDocx / EditPptx)
|
||||
|
||||
ローカル workspace の Office 文書・PDF を読み込むツール群。
|
||||
|
||||
@@ -108,9 +108,9 @@ merges: B2:D4
|
||||
|
||||
`max_style_ranges`(デフォルト 250)で全シートを通じた range 出力数を制限する。上限に達した場合は `[styles truncated: max_style_ranges=N reached]` を表示し、以降のスタイルは省略される。値テーブルが先に出力されるため、スタイルセクションがトークン上限で切り捨てられても値は失われない。
|
||||
|
||||
**編集には python + openpyxl を推奨:**
|
||||
**編集は EditExcel を使う:**
|
||||
|
||||
ReadExcel はスタイルを読むだけで、書き込みは行わない。既存スタイルを保持したまま値を編集する場合は `Bash` ツールで python + openpyxl を使うこと(openpyxl は触れていないセルのスタイルを保持する)。
|
||||
値・数式だけでなく、セル結合、書式、シート追加、ガントの着色にも対応する。独自のPythonスクリプトやopenpyxlより先に `EditExcel` を使うこと。詳細編集では保存前の構造検査と数式再計算まで一括で行う。
|
||||
|
||||
### ReadDocx
|
||||
|
||||
@@ -200,11 +200,281 @@ SplitDocxSections({ file_path: "input/long-spec.docx" })
|
||||
| Word(長文・章構成) | SplitDocxSections → Read | - |
|
||||
| PowerPoint | ReadPPTX | - |
|
||||
|
||||
### EditExcel
|
||||
|
||||
`.xlsx` を編集する。**`edits` と `operations` は別モードであり、同時に指定しない。**
|
||||
|
||||
#### 軽量なセル編集(edits / rows / merges / columns)
|
||||
|
||||
値・数式・基本的な書式・セル結合・列幅を直し、複雑なブックの未対応部品を保持したい場合に使う。
|
||||
|
||||
```js
|
||||
EditExcel({
|
||||
path: "output/sales.xlsx",
|
||||
edits: [
|
||||
{ cell: "B2", value: 250 },
|
||||
{ sheet: "集計", cell: "C10", formula: "SUM(C2:C9)" },
|
||||
{ cell: "D5", value: null },
|
||||
// 値はそのまま、書式だけを変える
|
||||
{ cell: "A1", style: { bold: true, fill_color: "#FFFF00", alignment: { horizontal: "center" } } },
|
||||
// 表示形式(数値の見せ方)
|
||||
{ cell: "B3", style: { number_format: "#,##0" } },
|
||||
{ cell: "C3", style: { number_format: "yyyy/mm/dd" } }
|
||||
],
|
||||
rows: [{ row: 1, height: 28 }],
|
||||
merges: [{ range: "A1:C1", merged: true }],
|
||||
columns: [{ columns: "A:C", width: 18 }]
|
||||
})
|
||||
```
|
||||
|
||||
対象シートのOOXMLだけを差し替えるため、書式・条件付き書式・入力規則・結合セル・図・グラフ・ピボット・マクロ・他シートを保持する。数式は `fullCalcOnLoad=1` を付け、古い `calcChain.xml` を除去する。計算結果の検証は行わない。
|
||||
|
||||
**書式(`edits[].style`)**
|
||||
|
||||
指定した項目だけが、そのセルの今の書式に重なる。書かなかった項目は元のまま残る。
|
||||
`value` も `formula` も省くと、値には触れず書式だけを変える。
|
||||
|
||||
重ねる土台は**セル → 行 → 列**の順に探す。セルに書式が付いていなくても、行(`customFormat="1"` の付いた行の書式)や列(`<col style="...">`)から受け継いだ見た目があればそれを土台にする。どこにも無ければ既定の書式から組み立てる。
|
||||
|
||||
| 項目 | 意味 |
|
||||
|---|---|
|
||||
| `bold` | 太字。`false` で解除 |
|
||||
| `font_color` | 文字色 `"#RRGGBB"` または `"#AARRGGBB"`。`null` で既定色に戻す |
|
||||
| `fill_color` | 背景色 `"#RRGGBB"` または `"#AARRGGBB"`。`null` で塗りなし |
|
||||
| `border` | 罫線。`{ edges, style, color }`。`edges` 省略時は 4 辺。`style` 省略時は `thin`。`color` 省略時と `null` は既定色。`null` で 4 辺とも消す |
|
||||
| `alignment` | 揃え。`{ horizontal, vertical, wrap_text }`。`null` で揃えの指定を消す。`{}`(中身なし)は断る |
|
||||
| `number_format` | 表示形式。Excel の書式コードをそのまま渡す(`"#,##0"` `"0.0%"` `"yyyy/mm/dd"` `'"¥"#,##0'`)。`null` か `"General"` で標準に戻す。255 文字まで |
|
||||
|
||||
**行の高さ(`rows`)**
|
||||
|
||||
`{ sheet, row, height }`。`row` は 1 始まり、`height` はポイント(0 より大きく 409.5 以下= Excel の上限と同じ)。`null` で自動に戻す。
|
||||
|
||||
書式表(`xl/styles.xml`)へは**末尾にだけ**組を足す。既存セルが指す番号は 1 つも動かないので、触っていないセルの見た目は変わらない。同じ書式を何セルに、何回の保存にまたいで掛けても、書式表は増えない(元からある組も突き合わせて使い回す)。
|
||||
|
||||
表示形式の番号は、規格が決めた組み込みの席(0〜163)に載っているものはそのまま使い、それ以外は 164 から採番して `numFmts` に載せる。日付・時刻・通貨は組み込みの番号に**寄せない**——組み込みの番号は見え方が表示側の地域設定で変わり、規格と Excel で中身が違う番号もあるため、渡した書式コードをそのまま載せる。
|
||||
|
||||
ブックが `numFmts` で組み込みの席を自分の書式コードに**上書きしている**ことがある(変換ツールを通ったファイルなど)。その番号には寄せず、164 以上へ新しく採番する。上書きされた番号へ寄せると、頼んだ見せ方と違うものが読み手に見える。
|
||||
|
||||
**セルの結合(`merges`)**
|
||||
|
||||
`{ sheet, range, merged }`。`range` は `A1:C1`(左上:右下)、`merged` は `true` で結合・`false` で解除。
|
||||
|
||||
| 断るもの | 例 | 理由 |
|
||||
|---|---|---|
|
||||
| 範囲になっていない | `A1` | 1 セルの結合は Excel にも無い |
|
||||
| 逆順 | `C1:A1` | 左上と右下が入れ替わっている |
|
||||
| シートの外 | `A1:XFE1` / `A1:A1048577` | Excel の列数(16384)・行数(1048576)を超える |
|
||||
| 重なる結合 | `A1:C1` と `B1:D1` | 重なった結合はブックが開けなくなる。既にある結合との重なりも、同じ呼び出しの中の重なりも断る |
|
||||
| 二重の結合 | 既に `A1:C1` があるところへ `A1:C1` | 同上 |
|
||||
| 結合されていない範囲の解除 | 結合の無い `D1:E1` を `merged: false` | 黙って何もしないと、解除できたと誤解する |
|
||||
| 中身が入ったセルを巻き込む結合 | `B1` に値があるまま `A1:C1` | **Excel は結合すると左上以外の中身を捨てる**。黙って消さず、どのセルが該当するかを挙げて断る |
|
||||
| 位置を並び順で書いているシートの結合 | `<c>` に `r` が無い行がある | どのセルが範囲に入るか読めない。何が消えるか分からないまま結合するより断る(その行にかかる結合だけ) |
|
||||
|
||||
最後の 1 件は、同じ呼び出しの `edits` でそのセルを空にすれば通る(`edits` → `columns` → `merges` の順に当てるため)。
|
||||
|
||||
```js
|
||||
EditExcel({
|
||||
path: "output/sales.xlsx",
|
||||
edits: [{ cell: "B1", value: null }, { cell: "C1", value: null }],
|
||||
merges: [{ range: "A1:C1", merged: true }]
|
||||
})
|
||||
```
|
||||
|
||||
解除と結合を同じ呼び出しに書いた場合は**解除が先**。`A1:C1` を解いて `A1:B1` に結び直す、が 1 回で書ける。
|
||||
|
||||
`<mergeCells count="N">` の N は実際の件数に合わせて書き直す。最後の 1 件を解いたときは `<mergeCells>` という入れ物ごと消す(空の入れ物は ECMA-376 違反)。
|
||||
|
||||
**列の幅(`columns`)**
|
||||
|
||||
`{ sheet, columns, width }`。`columns` は `"A:C"`(範囲)または `"B"`(1 列)。`width` の単位は**標準書体の「0」の文字幅**で、ピクセルでもポイントでもない(ECMA-376 の `CT_Col/@width`)。0 より大きく 255 以下。`null` で幅の指定を外す(既定幅に戻る)。
|
||||
|
||||
`<col>` の `min` / `max` は範囲なので、1 本が複数の列をまとめていることがある。3 列目だけ幅を変える指定は、1-5 をまとめた 1 本を 1-2 / 3-3 / 4-5 の 3 本に割ってから当てる(範囲が重なった `<col>` を並べると Excel の挙動が定まらない)。割った先には列の書式(`style`)・非表示(`hidden`)などの属性をすべて引き継ぐ。
|
||||
|
||||
幅を書くときは `customWidth="1"` も一緒に書く。付けないと Excel は幅を無視して既定幅で描く。逆に `bestFit`(自動で決めた幅、という印)は落とす。
|
||||
|
||||
**`worksheet` の子要素の並び順**
|
||||
|
||||
`<cols>` は `sheetData` の**前**、`<mergeCells>` は**後ろ**にあり、ECMA-376 の `CT_Worksheet` は子要素の順番を決めている(`sheetPr` → `dimension` → `sheetViews` → `sheetFormatPr` → `cols` → `sheetData` → `sheetCalcPr` → `sheetProtection` → `protectedRanges` → `scenarios` → `autoFilter` → `sortState` → `dataConsolidate` → `customSheetViews` → `mergeCells` → `phoneticPr` → `conditionalFormatting` → … → `extLst`)。順番を破ると Excel が修復を求める。要素が無いシートへ足すときは、`<cols>` は `sheetData` の直前、`<mergeCells>` は**後ろに来る要素のうちいちばん手前にあるもの**の直前へ入れる。並びの根拠は `src/office/xlsx-sheet-layout.ts` の冒頭に全 39 個を列挙してある(数えたのは ECMA-376 4th edition の **transitional** 版。`legacyDrawingHF` と `picture` の間に `drawingHF` が入る。strict 版はこれを持たず 38 個)。
|
||||
|
||||
数式の計算結果の検証、シート追加、ガントの着色は引き続き `operations` を使う。
|
||||
|
||||
**コピーされた式のおおもとを書き換えるとき(#1351 段 2)**
|
||||
|
||||
Excel で式を下へコピーした列は、ファイルの中では 1 つのセルだけが式の文面と範囲を持ち(`<f t="shared" ref="C2:C4" si="0">`)、残りはその番号を指すだけになる(`<f t="shared" si="0"/>`)。この**本体**のセルへ値や数式を書くと、そのままでは同じかたまりの残りのセルから式も計算結果も消える。
|
||||
|
||||
そこで、本体へ書く前に**追随するセルの式を実体化する**。本体の式を各セルの位置に合わせて訳し(相対参照はずれ、`$` の付いた側は動かない)、`<f>` に全文で書き込んでから本体を書き換える。計算済みの値(`<v>`)はそのまま残す。`edits` と `operations` のどちらから来ても同じ扱いで、扱いは `src/office/shared-formula.ts` の 1 か所に寄せてある。
|
||||
|
||||
訳せない式は、段 1 と同じく断ってファイルに手を付けない。
|
||||
|
||||
| 断る場面 | 例 |
|
||||
|---|---|
|
||||
| 列や行をまるごと指す参照が混じっている | `SUM(A:A)` / `SUM(1:1)` |
|
||||
| 訳した結果が表の外(行 1 や列 A より手前)へ出る | 本体が範囲の左上に無い書類 |
|
||||
|
||||
- `edits`: `Cannot rewrite the copied formula from C2 (the block C2:C4) for C3 ...` を返す
|
||||
- `operations`: `violations[].code` が `SHARED_FORMULA_MASTER`、`sheet` と `cell` に場所が入る
|
||||
|
||||
これとは別に、**壊れた書類**として断る場面が 3 つある。どれも Excel 本体は書かない形で、外から来た書類への歯止め。
|
||||
|
||||
| 断る場面 | なぜ決められないか |
|
||||
|---|---|
|
||||
| 本体が範囲と番号を持ちながら式の文面を持たず(`<f t="shared" ref="C2:C4" si="0"/>`)、その番号を指すセルが残っている | 配る中身が無い。通すと本体の `ref` と `si` の定義ごと消え、残りのセルの `si` が指す先を失う(規格 ISO/IEC 29500 18.3.1.40 は本体が文面を持つと定めている) |
|
||||
| 番号を名乗るセルが、その番号のどの本体の `ref` 範囲にも入っていない | どちらのかたまりへ配るのが正しいか決まらない |
|
||||
| 同じ番号の本体が 2 つあり、`ref` 範囲が重なっている | 重なりの中のセルがどちらの持ち物か決まらない |
|
||||
|
||||
**追随するセルの持ち主は、番号(`si`)だけでは決まらない。**番号と `ref` 範囲の両方で決める。番号だけで決めると、番号がぶつかった 2 つのかたまりが混線する——`C2:C3` と `E2:E3` がどちらも `si="0"` の書類で `C2` だけを書き換えると、`E3` に C のかたまりの式が入り、触っていない E のかたまりが黙って壊れる。判定は `src/office/xlsx-sheet-edit.ts` の `assignSharedFormulaFollowers` の 1 か所にある。
|
||||
|
||||
**番号がぶつかった書類は `edits` なら通るが、`operations` は 1 マスも書けない。**扱いが分かれるのは、`operations` が exceljs を通すため。exceljs は読み込みの時点で追随するセルを 1 つの本体へ結びつけてしまい、**番号がぶつかっていた事実そのものがモデルから消える**。しかも結び先の選び方が読み手ごとに違う(exceljs は**あとに**見た本体が勝ち、Excel / LibreOffice は**先に**見た本体が勝つ)。`operations` はブックを丸ごと読み込んで丸ごと書き戻すので、**書き換えたマスと壊れるマスは別**になる。コピー式と関係のない `A1` に値を 1 つ入れるだけで、触っていないかたまりの式が別のかたまりの式に置き換わる(実測: `C3` が 33 から 90、`E3` が 40 から 630。別のシートを編集しても同じ)。
|
||||
|
||||
そこで `operations` は、**ファイルを開いた時点で**生のシート XML をシートごとに 1 回ずつ見て、下の 5 つのどれかがあれば `SHARED_FORMULA_MASTER` で断る。書き込み先を組み立てる前に断るので、ファイルには手を付けない(#1378 / #1376)。
|
||||
|
||||
| 断る並び | どうなるはずだったか | `edits` は |
|
||||
|---|---|---|
|
||||
| 同じ番号の本体が 2 つ以上ある | 触っていないかたまりの式が化ける | 通る(`ref` 範囲で持ち主を決める) |
|
||||
| `ref` 範囲からも持ち主が決まらない(上の表の 2 つ) | 別のかたまりの式が流れ込む | その本体を書く指示だけ断る |
|
||||
| 番号を名乗るセルが残っているのに、その番号の本体がシートに 1 つも無い | 中身の無い式(`<f/>`)が書かれ、Excel の表示が `Err:520` になる | 通る(触らないセルは書き換えない) |
|
||||
| 本体が、その番号を名乗るセルより**後ろ**に並んでいる | 同上。exceljs は読みながら採番表を作るので、まだ登録されていない番号を引いたセルの持ち主が `undefined` になる | 通る(`ref` 範囲で持ち主を決めるので並び順に依らない) |
|
||||
| 本体が式の文面を持たず、その番号を指すセルが残っている | exceljs が式を平行移動しようとして例外を出す(保存できない) | その本体を書く指示だけ断る |
|
||||
|
||||
**並び順まで見る。**シートにその番号の本体が「居るか」だけを見ると、後ろに並んだ本体の書類が素通りして値が消える(#1378 レビュー P1-1)。判定は `<c>` を文書順に 1 回歩き、本体を見た時点でその番号を登録済みにして、**その時点で登録済みの番号だけ**を親ありと見なす。
|
||||
|
||||
判定は `src/office/xlsx-sheet-edit.ts` の `findSharedFormulaSheetDoubts` の 1 か所。番号のぶつかりと持ち主の判定はさらに `findSharedFormulaOwnershipDoubts` / `assignSharedFormulaFollowers` に相乗りしていて、`edits` 側と同じ判定を使う。断り文句は「`edits` なら通る」のか「本体に触らなければ通る」のかで書き分けている。
|
||||
|
||||
**共有式の番号はシートごとに閉じている。**普通のブックは、シートが何枚あっても素で「どのシートも `si="0"`」になる。だから断る判定は**シート 1 枚ぶんの XML**を見る。複数シートのブックがどれも `si="0"` を使っていても通る。
|
||||
|
||||
| 経路 | 番号がぶつかった書類 |
|
||||
|---|---|
|
||||
| `edits` | 通る。`ref` 範囲で持ち主を決めて実体化する(範囲からも決まらない書き方なら、こちらも断る) |
|
||||
| `operations` | どのマスを書く指示でも `SHARED_FORMULA_MASTER` で断る。ファイルには手を付けない |
|
||||
|
||||
配列数式(`t="array"`)のもとになっているセルは、この扱いの対象外(#1363)。
|
||||
|
||||
|
||||
#### 検証つき詳細編集(operations)
|
||||
|
||||
色・レイアウト・シート追加・ガント、または数式の計算結果検証が必要な場合に使う(セルの結合と列の幅は上の `merges` / `columns` で足りる)。既存ファイルを直接上書きせず、まず別名へ出すのを推奨。
|
||||
|
||||
```js
|
||||
EditExcel({
|
||||
path: "input/plan.xlsx",
|
||||
output_path: "output/plan-checked.xlsx",
|
||||
operations: [
|
||||
{ op: "set_values", sheet: "計画", range: "A2:B3",
|
||||
values: [["設計", 0.5], ["実装", 0.25]] },
|
||||
{ op: "set_formula", sheet: "計画", cell: "C2",
|
||||
formula: "B2*100", expect: { equals: 50, tolerance: 0.001 } },
|
||||
{ op: "merge", sheet: "計画", range: "A1:C1" },
|
||||
{ op: "style_range", sheet: "計画", range: "A1:C1",
|
||||
style: { fill: "#1F4E78", font_color: "#FFFFFF", bold: true } },
|
||||
{ op: "set_column_width", sheet: "計画", columns: "A:C", width: 18 },
|
||||
{ op: "freeze_panes", sheet: "計画", rows: 1, columns: 0 }
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
主な `op`:
|
||||
|
||||
| op | 必須項目 | 用途 |
|
||||
|---|---|---|
|
||||
| `add_sheet` | `name` | シート追加 |
|
||||
| `set_values` | `sheet, range, values` | 二次元配列を範囲へ書く |
|
||||
| `set_formula` | `sheet, cell, formula` | 1セルへ数式。任意の `expect` で結果検証 |
|
||||
| `fill_formula` | `sheet, range, formula` | 相対参照をずらしながら数式を範囲へコピー |
|
||||
| `merge` / `unmerge` | `sheet, range` | セル結合・解除(保持したまま直したいときは上の `merges`) |
|
||||
| `style_range` | `sheet, range, style` | 背景・文字・罫線・太字・表示形式・配置 |
|
||||
| `set_column_width` | `sheet, columns, width` | 列幅(保持したまま直したいときは上の `columns`。上限 255 は共通) |
|
||||
| `set_row_height` | `sheet, rows, height` | 行高(`height` は 0 より大きく 409.5 以下。上の `rows` と同じ上限) |
|
||||
| `freeze_panes` | `sheet, rows, columns` | ウィンドウ枠固定 |
|
||||
|
||||
色は `#RRGGBB` / `#AARRGGBB`。数式先頭の `=` は省略可能。`expect` は単一値、または `{ equals, tolerance }`。不一致なら保存しない。
|
||||
|
||||
ガントは通常の `operations` で日付・開始日・終了日・進捗を書いたうえで `gantt` を指定する。
|
||||
|
||||
```js
|
||||
gantt: {
|
||||
sheet: "計画",
|
||||
timeline_header: "D1:AH1",
|
||||
task_rows: "2:20",
|
||||
start_column: "A",
|
||||
end_column: "B",
|
||||
progress_column: "C",
|
||||
progress_scale: "fraction",
|
||||
holidays: ["2026-08-11"],
|
||||
roles: {
|
||||
weekend: { fill: "#EEEEEE" },
|
||||
holiday: { fill: "#FFD9D9" },
|
||||
planned: { fill: "#9DC3E6" },
|
||||
completed: { fill: "#70AD47" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
保存前に、入力OOXMLの危険・非対応機能検査、操作競合、セル数上限、数式の静的検査、LibreOffice再計算、`#VALUE!` 等の数式エラー、期待値、数式と結合構造の保持を検証する。どれかが失敗すると候補を削除し、JSONの `phase` / `violations[].code` / `sheet` / `cell` に理由を返す。`dry_run: true` なら検証のみで保存しない。
|
||||
|
||||
`violations[].code` は原因の所在で読み分ける。`SHARED_FORMULA_MASTER` は入力ファイル(コピーされた式のかたまりが扱えない形になっている。おおもとへ書こうとして残りのセルの式を訳せなかった場合と、**書き込み先とは関係なくファイルを開いた時点で断った**場合の両方がある。上記)、`INPUT_INVALID` は渡した引数、`PACKAGE_INVALID` / `UNSUPPORTED_WORKBOOK_FEATURE` は入力ファイル、`INTERNAL_ERROR` は**こちら側の実行時例外**(再計算プロセスが起動できない等)で、ファイルが壊れていることを意味しない。`INTERNAL_ERROR` を「ファイルを直してから再実行」の根拠に使わないこと。ただし、以前はファイル側が原因でも `INTERNAL_ERROR` になる形が 1 つあった——式の文面を持たない共有式の本体(上記)で、exceljs が式を平行移動しようとして例外を出していた。いまはファイルを開いた時点で `SHARED_FORMULA_MASTER` として断るので、この形は `INTERNAL_ERROR` にならない(#1376)。
|
||||
|
||||
新規保存では `output_path` が既に存在すると拒否する。同じ入力を上書きする場合だけ `overwrite: true` と読取時点の `expected_source_sha256` が必要。編集中に元ファイルが変わった場合も確定しない。
|
||||
|
||||
**詳細編集の制約**: `.xlsx` のみ。マクロ、外部リンク、ピボット、スライサー、グラフ、OLE、Excelテーブル、署名、動的配列などを含むブックはfail closedで拒否する。複雑なブックの値だけを変える場合は `edits` を使う。`readonly/` 配下は常に書き換え不可。
|
||||
|
||||
### EditDocx
|
||||
|
||||
既存の .docx の**指定した段落だけ**を書き換える。
|
||||
|
||||
```
|
||||
EditDocx({ path: "output/report.docx", list_paragraphs: true }) // まず番号を知る
|
||||
EditDocx({
|
||||
path: "output/report.docx",
|
||||
edits: [{ paragraph: 3, text: "書き直した本文です。" }]
|
||||
})
|
||||
```
|
||||
|
||||
| 引数 | 意味 |
|
||||
|---|---|
|
||||
| `path` | 対象の .docx(workspace 相対) |
|
||||
| `list_paragraphs` | `true` なら書き換えず、番号つきの段落一覧だけ返す |
|
||||
| `edits[].paragraph` | 0 始まりの段落番号(空段落も 1 件として数える) |
|
||||
| `edits[].text` | 差し替える文字列。空文字なら段落を残して中身だけ消す |
|
||||
| `output_path` | 別名保存先。省略時は上書き |
|
||||
|
||||
**何が保たれるか**: 本文の項目(`word/document.xml`)だけを差し替える。スタイル定義・表・画像・ヘッダー / フッター・変更履歴・埋め込みオブジェクトは元のバイト列のまま残る。段落の書式(見出しなど)と、その段落の先頭の文字書式も引き継ぐ。
|
||||
|
||||
**制約**: 段落まるごとの差し替えなので、1 段落の中で途中から書式が変わっていた場合、その変化は先頭の書式に統一される。引き継ぐのは**最初の run の書式**で、run が 1 つも無い段落のときだけ段落記号の書式を引き継ぐ(そのとき、段落記号にしか置けない挿入・削除・移動・書式変更の印は落とす)。**書き換えた段落の中にあった変更履歴(挿入・削除・移動の印)・コメントの錨・しおり・ハイパーリンク・コンテンツコントロールは、出力から消える**(コメントや脚注の本体は別ファイルに残るが、本文からの参照は失われる)。校閲中の文書を書き換えるときは、消してよいかを先に確認すること。段落の追加・削除、表の行・列・セルの追加や削除・結合はできない。**表の中の段落は段落番号に入り、書き換えられる**(`list_paragraphs` が返した番号をそのまま指定する)。`.doc`(旧形式)は非対応。
|
||||
|
||||
### EditPptx
|
||||
|
||||
既存の .pptx の**指定した段落だけ**を書き換える。
|
||||
|
||||
```
|
||||
EditPptx({ path: "output/deck.pptx", list_paragraphs: true }) // まずスライドと段落の番号を知る
|
||||
EditPptx({
|
||||
path: "output/deck.pptx",
|
||||
edits: [{ slide: 2, paragraph: 0, text: "第2四半期の実績" }]
|
||||
})
|
||||
```
|
||||
|
||||
| 引数 | 意味 |
|
||||
|---|---|
|
||||
| `path` | 対象の .pptx(workspace 相対) |
|
||||
| `list_paragraphs` | `true` なら書き換えず、スライドごとの段落一覧だけ返す |
|
||||
| `edits[].slide` | 1 始まりのスライド番号(**見た目の並び順**) |
|
||||
| `edits[].paragraph` | 0 始まりの段落番号(空段落も 1 件として数える) |
|
||||
| `edits[].text` | 差し替える文字列。空文字なら段落を残して中身だけ消す |
|
||||
| `output_path` | 別名保存先。省略時は上書き |
|
||||
|
||||
**スライド番号は並び順で数える**。項目名の番号(`slide3.xml`)とは一致しないことがある——スライドを並べ替えてもファイル名は変わらず、順序だけが変わるため。`list_paragraphs` が返す番号をそのまま使うこと。
|
||||
|
||||
段落番号は図形をまたいだ通し番号。タイトルも本文も表の中の文字も、同じ 1 本の並びに入る。
|
||||
|
||||
**何が保たれるか**: 指定したスライドの項目(`ppt/slides/slideN.xml`)だけを差し替える。配置・図形・画像・アニメーション・スライドマスター・ノート・埋め込みオブジェクトは元のバイト列のまま残る。段落の書式と、その段落の先頭の文字書式も引き継ぐ。
|
||||
|
||||
**制約**: 段落まるごとの差し替えなので、1 段落の中で途中から書式が変わっていた場合、その変化は先頭の書式に統一される。段落・スライドの追加や削除、図形の移動はできない(新規作成は AddSlide / BuildPptx)。`.ppt`(旧形式)は非対応。
|
||||
|
||||
## 注意
|
||||
|
||||
- すべて workspace 内のローカルファイル(`input/` または `output/`)が対象
|
||||
- URL 指定不可 → DownloadFile で先にローカル保存
|
||||
- 全ツール read-only(書き込まない)
|
||||
- EditExcel / EditDocx / EditPptx 以外は read-only(書き込まない)
|
||||
|
||||
## ファイルサイズ上限
|
||||
|
||||
@@ -212,12 +482,66 @@ Read 系ツールは悪意あるファイル / リソース枯渇対策として
|
||||
|
||||
| ツール | デフォルト | config キー |
|
||||
|---|---|---|
|
||||
| ReadExcel | 10 MB | `tools.office_excel_max_size_mb` |
|
||||
| ReadDocx | 10 MB | `tools.office_docx_max_size_mb` |
|
||||
| ReadExcel / EditExcel | 10 MB | `tools.office_excel_max_size_mb` |
|
||||
| ReadDocx / EditDocx | 10 MB | `tools.office_docx_max_size_mb` |
|
||||
| ReadPdf | 10 MB | `tools.office_pdf_max_size_mb` |
|
||||
| ReadPPTX | 50 MB | `tools.office_pptx_max_size_mb` |
|
||||
| ReadPPTX 展開後 | 200 MB | `tools.office_pptx_max_uncompressed_mb` |
|
||||
| ReadPPTX / EditPptx | 50 MB | `tools.office_pptx_max_size_mb` |
|
||||
| SplitExcelSheets | 10 MB | `tools.office_excel_max_size_mb` |
|
||||
| SplitDocxSections | 10 MB | `tools.office_docx_max_size_mb` |
|
||||
| 画面の Excel セル保存 | 10 MB | `tools.office_excel_max_size_mb` |
|
||||
| 画面の Excel プレビュー | 50 MB | `tools.office_preview_max_size_mb` |
|
||||
| 展開後(下記 10 経路すべて) | 200 MB | `tools.office_pptx_max_uncompressed_mb` |
|
||||
|
||||
PPTX の「展開後」は ZIP bomb 検知用で、ZIP 内の全エントリの非圧縮合計サイズに対する閾値。超過時は `ZIP bomb detected: ...` エラーとなる。
|
||||
「展開後」は ZIP bomb 検知用で、ZIP 内の全エントリの非圧縮合計サイズに対する閾値。超過時は、エージェント向けの経路では `ZIP bomb detected: ...` エラーとなる(画面のプレビューだけは 413 と数値を返し、文言は UI が翻訳して組む)。キー名は ReadPPTX 由来だが、**OOXML を開く経路すべてで共有する**。判定は ZIP ヘッダが申告するサイズの合計で行う。`EditExcel` の `operations` モードだけは共通の関所ではなく preflight を通り、実際に展開して測るが、上限は同じ config キーを読む。
|
||||
|
||||
| 展開後の上限が掛かる経路 | 備考 |
|
||||
|---|---|
|
||||
| `Read` → ReadExcel / ReadDocx / ReadPPTX | 読む口。ReadExcel / ReadDocx は #1084 段階 2 で追加 |
|
||||
| SplitExcelSheets / SplitDocxSections | #1084 段階 2 で追加(それまでファイルサイズ上限すら無かった) |
|
||||
| EditExcel / EditDocx / EditPptx | 書き換える口 |
|
||||
| 画面の Excel プレビュー(`GET .../files/office-preview`) | #1084 段階 2 で追加(それまで上限が 1 つも無かった) |
|
||||
| 画面の Excel セル保存(`PUT .../files/xlsx-cells`) | |
|
||||
|
||||
以前この関所が無かった 5 経路(読む口 2 つ・分割ツール 2 つ・画面のプレビュー)は、**嘘の申告をしなくても増幅した**——正当なファイルに大きなエントリを正直に足すだけで通っていた(issue #1084 段階 2 で塞いだ)。
|
||||
|
||||
**画面のプレビューだけ、ファイルサイズのつまみが別**(`tools.office_preview_max_size_mb`、既定 50MB)。エージェント側の 10MB は「読んで LLM に渡す量」を想定した値で、人が眺めるだけの経路には小さい——この本の前まで上限が無く映せていた実務ブック(30 万行の受注明細で約 20MB)が、共有すると今日から映らなくなる(#1084 レビュー P2-1)。展開後サイズと項目数はエージェントと共有のまま。実メモリを縛っているのはそちらなので、片方だけ動かす理由が無い。
|
||||
|
||||
**既定値で残る量**: ファイル 10MB・展開後の申告 61MB・項目 10 件のブックが、全部の関所を通ったうえで常駐を約 588MB 増やす(嘘の申告は無し)。実質的に効いている歯止めはファイルサイズで、展開後 200MB の関所は実メモリを縛れていない(展開後サイズと実メモリの比が約 9.6 倍あるため)。ここを実測で縛るのは #1084 段階 3 の担当。
|
||||
|
||||
**上げるときの副作用**: `operations` モードは申告値ではなく実際に展開して測るため、このキーはその経路が**展開に使ってよいメモリの実質的な上限**でもある。値を 200MB から大きく上げると、1 回の編集が確保しうるメモリの上限も同じだけ上がる(以前は経路側の固定値 200MB で頭打ちだった)。同時実行数と積んでいるメモリを見て決めること。
|
||||
|
||||
**まだ関所が乗っていない経路**もある。次の 2 つは対象外か、別の守りに任せている。
|
||||
|
||||
| 関所の無い経路 | 守り |
|
||||
|---|---|
|
||||
| `Read` から呼ばれる PDF / メール(`.msg`)の解析 | ZIP ではないので対象外。ファイルサイズ上限で守る |
|
||||
| 画面の PowerPoint / Word プレビュー変換 | 変換を別プロセス(LibreOffice)に出すので、本体プロセスは膨らまない |
|
||||
|
||||
展開後サイズとあわせて **ZIP エントリ数**も見る。中身が空のエントリを大量に並べたファイルは申告合計が 0 バイトになるため、サイズの閾値だけでは止まらない。超過時は `ZIP bomb detected: N entries exceeds limit of 10000` を返す(画面のプレビューは同じ判定で 413 と件数を返す)。
|
||||
|
||||
| 上限 | デフォルト | config キー |
|
||||
|---|---|---|
|
||||
| ZIP エントリ数(展開後サイズと同じ 10 経路 + `operations` モードの preflight) | 10,000 件 | `tools.office_max_entries` |
|
||||
|
||||
件数の判定は 2 段構えになっている(#1084 段階 1)。
|
||||
|
||||
1. **ZIP 末尾の中央ディレクトリが申告するエントリ数**で先に断る。エントリを 1 件も構築せずに済むので、断ると決まっているファイルにメモリを払わない。
|
||||
2. 申告が上限内なら、**中央ディレクトリを自前で最後まで歩き**(`src/office/zip-central-directory.ts`)、実際に並んでいる件数と申告を突き合わせる。食い違えば `Refusing to open this package: its index lists N entries but the trailer declares M...` で断る。
|
||||
|
||||
2 が要るのは、**中央ディレクトリの読み方が層によって非対称**だから。adm-zip は申告件数で止まり(`zipFile.js` の `readEntries` は `new Array(mainHeader.diskEntries)` から始まる)、ExcelJS が使う JSZip はエントリの並びが署名の続く限り読む。申告を小さく偽ると、関所からは 5 項目に見え、読み込み層からは 60,005 項目すべてが見えた——511KB のファイル 1 本で常駐 1.1GB、増幅は約 2,300 倍(issue #1084 の実測)。歩く部品は JSZip と同じ歩き方(末尾の記録はファイル全体の最後の署名、目録は署名が続く限り)に揃えてある。**adm-zip に件数を無視して読む口は無い**ので自前で書いている。
|
||||
|
||||
**歩けない形は断る(fail closed)。** 断ると決めた形は次のとおりで、いずれも実在の作り手が書く形ではない。
|
||||
|
||||
| 断る形 | 理由 |
|
||||
|---|---|
|
||||
| 実際の件数と申告件数が食い違う | 穴の本体。隠したエントリを持つ包み |
|
||||
| 末尾の記録(EOCD)が見つからない | ZIP ではない。または末尾が壊れている |
|
||||
| 中央ディレクトリが申告位置に無い(前にデータが付いた形=自己解凍形式など) | adm-zip は開けずに例外、JSZip は位置をずらして読み切る。**層ごとに見えるものが変わる**ので数えられない |
|
||||
| 中央ディレクトリが途中で切れている | 同上 |
|
||||
| ZIP64 だと申告しているのに、その記録が無い/表現できない大きさ | 同上 |
|
||||
|
||||
ZIP64(件数欄が 0xFFFF に張り付いて別の記録に載る形)は**正しく読んで通す**。ExcelJS / adm-zip / JSZip / LibreOffice / Info-ZIP / Python zipfile が書いた包みと、70,000 件の本物の ZIP64 が通ることを実測済み(`src/office/zip-central-directory.test.ts`)。
|
||||
|
||||
**まだ止まらないもの**: エントリの**申告サイズ**を小さく偽る形は、この関所では止まらない(申告値の合計で判定しているため)。そちらは実際に展開して測る形へ上げる段階 3 で塞ぐ(issue #1084)。
|
||||
|
||||
マクロ付きファイル(`.xlsm` / `.docm` / `.pptm` / `.xlsb`)は警告付きで読み込まれる(実行はされない)。
|
||||
|
||||
+91
-13
@@ -33,19 +33,56 @@
|
||||
| パラメータ | 説明 |
|
||||
|-----------|------|
|
||||
| `file_path` | workspace 内の相対 / 絶対パス(必須) |
|
||||
| `offset` | 読み始める行番号(0-indexed)。**テキスト読みのみ有効**(PDF/Office では無視される) |
|
||||
| `limit` | 読む最大行数。**テキスト読みのみ有効**(PDF/Office では無視される) |
|
||||
| `byte_offset` | 読み始めるバイト位置。改行の無い巨大ファイル向け。`offset` / `limit` と排他。テキスト読みのみ有効 |
|
||||
| `byte_length` | 読むバイト数。`byte_offset` と併用。テキスト読みのみ有効 |
|
||||
| `page_range` | **PDF のページ範囲**(例 `5-10` / `3`)。PDF を途中から / 一部だけ読むときは `offset` ではなくこれを使う |
|
||||
| `offset` | 読み始める行番号(0-indexed)。**行数以上を指定するとエラー**。**テキストと `.msg` 本文のみ有効**(PDF/Office に渡すと**エラーになり本文は返らない**) |
|
||||
| `limit` | 読む最大行数。**テキストと `.msg` 本文のみ有効**(PDF/Office に渡すと**エラーになり本文は返らない**) |
|
||||
| `byte_offset` | 読み始めるバイト位置。改行の無い巨大ファイル向け。**ファイルサイズ以上を指定するとエラー**。`offset` / `limit` と排他。テキスト読みのみ有効(PDF/Office/`.msg` に渡すとエラー) |
|
||||
| `byte_length` | 読むバイト数。`byte_offset` と併用。テキスト読みのみ有効(PDF/Office/`.msg` に渡すとエラー) |
|
||||
| `page_range` | **PDF のページ範囲 / PowerPoint のスライド範囲**(例 `5-10` / `3`)。途中から / 一部だけ読むときは `offset` ではなくこれを使う。**実在しないページ・スライドを指すとエラー**。PDF と PowerPoint 専用(テキストファイルに渡すとエラー) |
|
||||
|
||||
大きすぎる出力は残コンテキスト予算に収まるよう自動で切り詰められ、続きの読み方が
|
||||
注記される。
|
||||
|
||||
> **PDF/Office をずらして読むとき**: `offset` / `limit` / `byte_*` は**テキスト専用**で、
|
||||
> PDF・Excel・Word・PowerPoint では黙って無視される(=常に先頭から返る)。PDF は
|
||||
> `page_range`、Excel は `sheet` / `range` を使うこと。これらのフォーマットに
|
||||
> `offset` / `limit` を渡すと、出力の先頭に正しいパラメータへの誘導注記が付く。
|
||||
> **範囲指定は形式ごとに専用**。合わない組み合わせは **エラーになり、本文は一切返らない**。
|
||||
>
|
||||
> | 読むもの | 使うパラメータ |
|
||||
> |---|---|
|
||||
> | テキスト | `offset` / `limit`(行)、`byte_offset` / `byte_length`(バイト) |
|
||||
> | PDF | `page_range` / `query` |
|
||||
> | Excel | `sheet` / `range` |
|
||||
> | Word | `max_paragraphs` |
|
||||
> | PowerPoint | `page_range`(PDF と同じ名前) |
|
||||
> | `.msg` | `offset` / `limit`(**抽出した本文の行**) |
|
||||
>
|
||||
> 以前は合わない指定でも注記を添えて本文を返していたが、注記が読み飛ばされて
|
||||
> 先頭部分だけを「全部」と扱う事故が起きていたため、拒否に変えた(#1009)。
|
||||
> テキストにドキュメント用の指定を渡す逆向きは注記すら出なかった(#1041)。
|
||||
> どちらもエラー本文に、その形式で使うべきパラメータが書いてある。
|
||||
|
||||
### 範囲外の位置を指したとき(#1170)
|
||||
|
||||
`offset` がファイルの行数以上、または `byte_offset` がファイルサイズ以上のとき、
|
||||
`Read` は **エラーを返す**。以前は行指定なら空文字列を、バイト指定なら「範囲外」という
|
||||
文を、どちらも成功として返していたため、「読めたが中身が空だった=まだ続きがある」と
|
||||
誤解する事故が起きていた。
|
||||
|
||||
エラー本文には **指定した位置** と **そのファイルの実際の行数 / バイト数** が入る。
|
||||
続きが要る場合の手順はこの通り。
|
||||
|
||||
| 何が起きたか | 次にやること |
|
||||
|---|---|
|
||||
| `offset` が行数以上 | エラー本文の行数を上限として、`0` 以上その値未満で指定し直す |
|
||||
| `byte_offset` がサイズ以上 | エラー本文のバイト数未満で指定し直す |
|
||||
| そもそも全体を読みたい | `offset` / `byte_offset` を外して呼び直す(自動切り詰めが効く) |
|
||||
|
||||
境界の扱いも書いておく。
|
||||
|
||||
| 場合 | 結果 |
|
||||
|---|---|
|
||||
| `offset` = 行数(末尾ちょうど。読める行が 1 行も無い) | エラー |
|
||||
| 空ファイル(0 行)に `offset: 0` | エラー(`0 行` と返る) |
|
||||
| 空ファイルを `offset` 無しで読む | 成功(空文字列) |
|
||||
| `offset` は範囲内で `limit` が余る | 成功(読めた分だけ返る) |
|
||||
| 末尾が改行のファイル(`a\nb\n` は **2 行**)に `offset: 2` | エラー |
|
||||
|
||||
## フォーマット固有の詳細オプション(inline schema には無いが実行時に受理される)
|
||||
|
||||
@@ -57,10 +94,19 @@
|
||||
|-----------|------|
|
||||
| `sheet` | シート名(省略時は全シート) |
|
||||
| `range` | セル範囲(例 `A1:D10`、省略時はシート全体) |
|
||||
| `max_cells` | 最大セル数(デフォルト 1000) |
|
||||
| `max_cells` | 最大セル数(デフォルト 1000)。**ブック全体の合計**で数える |
|
||||
| `include_styles` | `true` で背景色 / フォント / 罫線 / 書式 / 結合を `### Styles` として追記(デフォルト false) |
|
||||
| `max_style_ranges` | `include_styles` 時の style range 上限(デフォルト 250) |
|
||||
|
||||
出力の先頭には、ブックにあるシートの一覧(名前・行数・列数・読めたかどうか)が
|
||||
`## Sheets` として必ず載る。`max_cells` はブック全体の合計なので、1 枚目で使い切ると
|
||||
2 枚目以降は 1 セルも読まれない。その場合、一覧のそのシートは `NOT READ` になり、
|
||||
途中で切れたシートには最後に読めたセルの番地が付く(#1165)。
|
||||
|
||||
> 一覧を先頭に置くのは、末尾が出力の文字数上限とトークン持ち分の両方で
|
||||
> 切られるため。以前は落ちたシートが出力に一切現れず、受け取った側からは
|
||||
> 1 枚しか無いブックに見えていた。
|
||||
|
||||
### Word(.docx)
|
||||
| オプション | 説明 |
|
||||
|-----------|------|
|
||||
@@ -82,11 +128,42 @@
|
||||
### PowerPoint(.pptx)
|
||||
| オプション | 説明 |
|
||||
|-----------|------|
|
||||
| `slideRange` | スライド範囲(例 `1-5` / `3`、省略時は全スライド) |
|
||||
| `page_range` | スライド範囲(例 `2-4` / `3`、省略時は全スライド)。inline schema にも公開済み |
|
||||
|
||||
スライドの範囲は PDF のページ範囲と**同じ `page_range`** で指定する。形式ごとに別の
|
||||
名前を覚えなくてよい。
|
||||
|
||||
`page_range` を外せば全スライドが返る。外した場合を除いて**黙って全体を返すことはない**。
|
||||
|
||||
| 渡したもの | 結果 |
|
||||
|---|---|
|
||||
| 実在するスライドを含む範囲(`2-3`、`4`) | その範囲だけ返す |
|
||||
| 実在しないスライドだけを指す範囲(5 枚のファイルに `9-10`) | **エラー**。本文に実際の枚数(`presentation has 5 slide(s)`)が入る |
|
||||
| 書式が読めない範囲(`abc` / `2-` / 空文字列 / 数値) | **エラー**(`Invalid page_range: ...`)。以前は黙って**全スライド**が返っていた |
|
||||
| `offset` / `limit` / `byte_offset` / `byte_length` | **エラー**。`page_range` を使う |
|
||||
|
||||
範囲外・書式不正のときは本文を一切返さない。先頭だけ返して成功にすると、受け取った
|
||||
側が「これで全部」と扱う(#1009 と同じ壊れ方)。
|
||||
|
||||
### Outlook メール(.msg)
|
||||
| オプション | 説明 |
|
||||
|-----------|------|
|
||||
| `offset` | **抽出した本文**の読み始め行(0-indexed)。本文の行数以上を指定するとエラー |
|
||||
| `limit` | 本文から読む最大行数 |
|
||||
|
||||
絞る単位を本文の行にしたのは、件名・送信者・宛先・添付の一覧が出力の骨格で、量を
|
||||
食っているのが本文だけだからである(自動切り詰めも本文にしか掛かっていない)。
|
||||
行なのでテキスト読みと同じ `offset` / `limit` をそのまま使う。
|
||||
|
||||
| 渡したもの | 結果 |
|
||||
|---|---|
|
||||
| `offset` / `limit`(本文の行数の内側) | ヘッダーと添付一覧は全件、本文はその範囲だけ返す |
|
||||
| 本文の行数以上の `offset` | **エラー**。本文に実際の行数が入る。**添付も保存しない** |
|
||||
| `byte_offset` / `byte_length` | **エラー**。`.msg` は OLE2 の複合ファイルで、ファイル上のバイト位置は抽出した本文と対応しない |
|
||||
|
||||
- 件名・送信者・宛先・本文を抽出する。
|
||||
- 添付ファイルは `input/` に保存し、保存先パスを出力に列挙する。
|
||||
- 添付ファイルは `input/` に保存し、保存先パスを出力に列挙する。**範囲を絞っても
|
||||
添付の一覧は全件残す**(保存先パスは呼び出し側が必要とするため)。
|
||||
- 読み取り専用フェーズ(verify 等、edit 不可)では添付を保存せず、その旨を注記する。
|
||||
- ファイル名衝突時は `name-1.ext` のように連番で退避し、既存ファイルは上書きしない。
|
||||
|
||||
@@ -98,8 +175,9 @@ Read({ file_path: "input/sales.xlsx", sheet: "Q1", range: "A1:D20" })
|
||||
Read({ file_path: "input/spec.docx", mode: "text" })
|
||||
Read({ file_path: "input/manual.pdf", page_range: "5-10" }) // PDF の 5-10 ページ目
|
||||
Read({ file_path: "input/manual.pdf", query: "認証", context_lines: 3 })
|
||||
Read({ file_path: "input/deck.pptx", slideRange: "2-4" })
|
||||
Read({ file_path: "input/deck.pptx", page_range: "2-4" }) // 2-4 枚目のスライド
|
||||
Read({ file_path: "input/mail.msg" }) // 添付は input/ へ
|
||||
Read({ file_path: "input/mail.msg", offset: 100, limit: 50 }) // 本文の 100 行目から 50 行
|
||||
Read({ file_path: "logs/huge.log", byte_offset: 0, byte_length: 4096 })
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# ReadAppDoc
|
||||
|
||||
MAESTRO のプロジェクト内ドキュメント(`docs/` / `pieces/`)を **symbolic name** で読み込むメタツール(常時利用可能)。主に Help アシスタントが、概念や操作手順をユーザーに答える前のリファレンス参照に使う。
|
||||
MAESTRO のプロジェクト内ドキュメント(`docs/` / `pieces/` / アプリ内ヘルプ)を **symbolic name** で読み込むメタツール(常時利用可能)。主に Help アシスタントが、概念や操作手順をユーザーに答える前のリファレンス参照に使う。
|
||||
|
||||
「定期実行はできますか」「共有はどうやるのですか」のような**製品の機能・使い方**の質問には、`help/<name>` で利用者向けヘルプ(アプリ内の Help パネルと同じ Markdown)を引く。
|
||||
|
||||
ワークスペース外の固定パスを読むため、通常の `Read` ツールでは到達できない。
|
||||
|
||||
@@ -17,6 +19,7 @@ MAESTRO のプロジェクト内ドキュメント(`docs/` / `pieces/`)を *
|
||||
| `docs/<path>` | `docs/<path>.md`(`.md` は省略可) | `docs/mcp`, `docs/architecture` |
|
||||
| `piece/<name>` | `pieces/<name>.yaml` | `piece/chat`, `piece/research` |
|
||||
| `tool/<name>` | `docs/tools/<name>.md`(`ReadToolDoc` と同等) | `tool/browseweb` |
|
||||
| `help/<name>` | `ui/src/content/help/<name>.md`(`.md` は省略可) | `help/06-schedules`, `help/01-intro` |
|
||||
|
||||
利用可能な doc が分からないときは、先に `ListAppDocs()` で一覧を取得する。
|
||||
|
||||
@@ -24,18 +27,21 @@ MAESTRO のプロジェクト内ドキュメント(`docs/` / `pieces/`)を *
|
||||
ListAppDocs() // まず一覧を見る
|
||||
ReadAppDoc({ name: "docs/mcp" }) // 該当 doc を読む
|
||||
ReadAppDoc({ name: "tool/browse-sessions" })
|
||||
ReadAppDoc({ name: "help/06-schedules" }) // 製品の機能・使い方
|
||||
```
|
||||
|
||||
## 読めないもの(allow-list / block)
|
||||
|
||||
セキュリティのため、開けるのは `docs/` `pieces/` `docs/tools/` 配下に限られ、以下は拒否される:
|
||||
セキュリティのため、開けるのは `docs/` `pieces/` `docs/tools/` `ui/src/content/help/` 配下に限られ、以下は拒否される:
|
||||
|
||||
- 内部向けトップレベル doc: `CLAUDE.md` / `AGENTS.md` / `README.md` / `architecture`
|
||||
- パストラバーサル(`..` を含む name)や allow-list 外の絶対パス
|
||||
- `help/` はサブディレクトリを掘れない(`help/<file>` の 1 段のみ)
|
||||
|
||||
## 制約
|
||||
|
||||
- 1 doc あたり **32KB** で打ち切り(超過分は末尾に omitted バイト数を注記)。
|
||||
- 1 doc あたりの打ち切りは、アプリ内ヘルプ(`help/<name>`)だけ **64KB**、それ以外(`docs/` / `piece/` / `tool/`)は **32KB**。超過分は末尾に omitted バイト数を注記する。
|
||||
- ヘルプが打ち切られたときは、注記に「続きはアプリ内のヘルプ(画面上部の「ヘルプ」→ 該当ページ)を開いて確認してください」と利用者へ案内するよう書いてある。読めなかった後半を推測で埋めないこと。
|
||||
- 存在しない name や不正な形式はエラーを返す。エラー時は `ListAppDocs()` で正しい名前を確認すること。
|
||||
|
||||
## 関連
|
||||
|
||||
+51
-5
@@ -1,6 +1,6 @@
|
||||
# ReadImage
|
||||
|
||||
画像ファイルを LLM に直接渡して内容を認識・説明させる。VLM(Vision Language Model)対応 worker でのみ使用可能。
|
||||
画像対応ワーカーが画像を直接受け取るか、対応ワーカーに問い合わせて説明文を受け取る。
|
||||
|
||||
## 基本
|
||||
|
||||
@@ -9,10 +9,48 @@ ReadImage({ file_path: "input/screenshot.png" })
|
||||
// → 画像内の文字・図表・物体について自然言語の説明が返る
|
||||
```
|
||||
|
||||
BrowseWeb で開いたページに載っていた画像は `source/images/` に自動で溜まる。そのまま渡せば読める。ページを開き直してスクリーンショットを撮る必要はない。索引 `source/index.jsonl` の `kind: "image"` の行に、どのページに載っていた画像かが入っている(詳細は `ReadToolDoc({ name: "BrowseWeb" })`)。
|
||||
|
||||
```js
|
||||
ReadImage({ file_path: "source/images/example.com-figure.png" })
|
||||
```
|
||||
|
||||
## 動作要件
|
||||
|
||||
- 呼び出し時の worker が `vlm: true` で設定されている必要がある
|
||||
- 設定がない場合、このツールはワークスペースで許可されていても利用不可(function definition から自動除外される)
|
||||
このツールが使える条件は 2 通りある。
|
||||
|
||||
| 条件 | 何が返るか |
|
||||
|---|---|
|
||||
| 実行中の worker が `capabilities.image: true` | 画像そのものが会話に注入される。以後の発言で画像を見ながら考えられる |
|
||||
| 実行中の worker は非対応だが `llm.offload.image: true` かつ `capabilities.image: true` の worker が居る | 対応 worker が書いた**説明文だけ**が返る。画像は会話に載らない |
|
||||
|
||||
どちらにも当てはまらないとき、このツールはワークスペースで許可されていても
|
||||
function definition から自動除外される。
|
||||
|
||||
### 回した実行(委譲モード)の性質
|
||||
|
||||
- **1 回きりの問い合わせ**。画像はエージェントのコンテキストに載らない
|
||||
- 「さっきの画像の右下は何色だった?」のような**追い質問はできない**。知りたいことは
|
||||
`prompt` に全部書く。`prompt` を省くと汎用の説明が返るだけで、価値が大きく落ちる
|
||||
- 観点を変えて聞きたくなったら、`prompt` を変えてもう一度呼ぶ
|
||||
- 呼び出し先は `capabilities.image: true` の worker から空きのある台が自動で選ばれる。全台が埋まって
|
||||
いれば空くまで待つ(タスクを中断すれば待ちからも抜ける)
|
||||
- ブラウザ操作の分割スクリーンショット(既定で最大 10 枚)をこのモードで読むと、
|
||||
1 枚ずつ独立した説明が 10 個返る。「この表の続きは次の画像」といった**横断した
|
||||
判断はできない**。必要なら 1 枚ずつ観点を指定して呼ぶ
|
||||
|
||||
### prompt の書き方
|
||||
|
||||
```js
|
||||
// 悪い例(委譲モードでは特に価値が低い)
|
||||
ReadImage({ file_path: "input/chart.png" })
|
||||
|
||||
// 良い例
|
||||
ReadImage({
|
||||
file_path: "input/chart.png",
|
||||
prompt: "縦軸の単位と、2024 年のバーの値を読み取って。凡例の項目名も全部挙げて。",
|
||||
})
|
||||
```
|
||||
|
||||
## 用途
|
||||
|
||||
@@ -29,6 +67,14 @@ ReadImage({ file_path: "input/screenshot.png" })
|
||||
|
||||
## 入力ファイル
|
||||
|
||||
- `input/` または `output/` 配下のローカル画像ファイル
|
||||
- `input/` / `output/` / `source/images/` 配下のローカル画像ファイル
|
||||
- URL 指定は不可(DownloadFile で先にローカル保存する)
|
||||
- 対応形式: png, jpg, jpeg, gif, webp, bmp
|
||||
|
||||
対応形式は 2 通りある。
|
||||
|
||||
| | 形式 | 渡し方 |
|
||||
|---|---|---|
|
||||
| そのまま渡す | png, jpg, jpeg, gif, webp, bmp | ファイルの中身をそのままモデルへ |
|
||||
| PNG に起こして渡す | svg, avif, tiff, tif, ico | こちら側で PNG に変換してからモデルへ。ファイル自体は元の形式のまま残る |
|
||||
|
||||
後者はページの魚拓が溜める形式(`source/images/`)に合わせたもの。**BMP を格納した古い形式の `.ico` だけは読めず**、理由付きのエラーが返る(いまの web のアイコンはほぼ PNG を格納しているので通常は当たらない)。
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# RenderMermaid
|
||||
|
||||
Mermaid 図の構文を検査し、PNG 画像に描画する。HTML ファイルを渡した場合は、図そのものを描画済みの SVG に置き換える。
|
||||
|
||||
**図を書いたら、必ずこのツールで確認してから完了すること。** 構文の誤りは実行するまで分からず、そのまま納品すると読み手の画面には図ではなくエラーが出る。
|
||||
|
||||
## 使い方
|
||||
|
||||
### 書いた図をその場で確かめる
|
||||
|
||||
```
|
||||
RenderMermaid({ code: "graph TD\n A[開始] --> B[終了]" })
|
||||
```
|
||||
|
||||
`output/diagrams/diagram.png` に画像が出る。**`ReadImage` でその画像を開き、意図した図になっているか目で確認すること。** 構文が通ることと、図が意図どおりであることは別の話で、後者は画像を見ないと分からない。
|
||||
|
||||
### ファイルの中の図をまとめて確かめる
|
||||
|
||||
```
|
||||
RenderMermaid({ path: "output/report.html" })
|
||||
```
|
||||
|
||||
ファイル内の Mermaid ブロックを全部検査する。`.md` と `.html` に対応。
|
||||
|
||||
HTML の場合は、成功した図を**描画済みの SVG に差し替える**(`embed: false` で抑制できる)。差し替え後は次のようになる:
|
||||
|
||||
- **その図を出すのに**スクリプトも外部通信も要らなくなる
|
||||
- ファイルプレビュー(スクリプトを止めている)でも図が出る
|
||||
- 元の Mermaid コードは `data-mermaid-source` 属性に残るので、後から直して作り直せる
|
||||
|
||||
**書き換えるのは図のブロックだけで、ファイルの他の部分には一切触れない。** `<script>` も同じで、図が SVG になったあと使われなくなった Mermaid の読み込みが残っていても、それは消さない。ページの中身はそれを書いた側のもので、消してよいかどうかをこのツールは判断できない。消したいなら自分で `Edit` する。
|
||||
|
||||
Markdown は差し替えない。プレビューが Mermaid をそのまま描画するので、画像にすると拡大や文字選択ができなくなり劣化する。
|
||||
|
||||
## 出力の読み方
|
||||
|
||||
```
|
||||
mermaid 11.15.0 — 2 diagram(s)
|
||||
OK #1: output/diagrams/output_report-1.png — open it with ReadImage to check the diagram is what you meant.
|
||||
FAILED #2: Parse error on line 3:
|
||||
line 3: A[設定を読む (config.yaml)] --> B[起動]
|
||||
```
|
||||
|
||||
画像の名前は元ファイルのパスから作る(`output/report.html` なら `output_report`)。同じ名前のファイルが別のフォルダにあっても、後から描いた図が前の図を上書きしない。
|
||||
|
||||
PNG は図の見た目の大きさの **2 倍の画素数**で書き出す(例: 見た目 126×195 の図なら 252×390 ピクセル)。拡大しても印刷しても文字がつぶれない。資料に貼るときは表示幅を実寸の半分に指定すると、意図した大きさで鮮明に出る。
|
||||
|
||||
ただし **120 万画素を超える図は、縦横の比を保ったままそこまで縮めて書き出す**(縦に十数段以上つながる図が目安。ラベルが長いほど早く届く)。画像は縮小されずにそのまま読み込まれるため、上限を置かないと 1 枚が大きすぎて `ReadImage` で開けなくなり、同時に開こうとした他の画像まで巻き添えで落ちる。長い図は縦に切って複数枚にすると、1 枚あたりが小さくなって鮮明さも保てる。
|
||||
|
||||
失敗した図は、エラー本文・行番号・その行の中身が返る。**その行を直して呼び直す**。
|
||||
|
||||
## よくある失敗
|
||||
|
||||
ラベルの中に括弧・角括弧・波括弧をそのまま書くと落ちる。**引用符で囲めば通る。**
|
||||
|
||||
```
|
||||
A[設定を読む (config.yaml)] ← 落ちる
|
||||
A["設定を読む (config.yaml)"] ← 通る
|
||||
```
|
||||
|
||||
これが最も多い原因。他に、日本語・長いラベル・多層のフローチャート・シーケンス図・クラス図・ガントは問題なく描画される(実測)。
|
||||
|
||||
## HTML を書くときの注意
|
||||
|
||||
生成する HTML に CDN の `<script src="...mermaid...">` を書かないこと。読み込むバージョンによって通る構文が変わるうえ、閲覧側に通信できない環境だと図がまったく出ない。
|
||||
|
||||
**最初から書かず、このツールに差し替えさせる。** 検査したバージョンと画面に出るものが同じになり、図のための通信は要らなくなる。書いてしまった読み込みはツールが消さないので、要らないと判断したら自分で消す(残っていても図の表示には影響しない)。
|
||||
|
||||
## 制限
|
||||
|
||||
- 1 回に扱える図は 30 個まで。1 図あたり 20,000 文字まで
|
||||
- 1 回の呼び出し全体で 60 秒まで。1 つの図が時間切れになった時点で残りは打ち切る(描画が止まったページは待っても戻らない)
|
||||
- 書き込みが許されていないタスクでは使えない(画像を保存できないため)
|
||||
- `readonly/` 配下のファイルは書き換えない。検査だけしたい場合は `embed: false` を付けて呼ぶ
|
||||
- 描画にはブラウザを使うため、1 回あたり数秒かかる。図ごとに呼び分けず、ファイル単位でまとめて呼ぶ方が速い
|
||||
@@ -0,0 +1,55 @@
|
||||
# RequestCaptchaHelp
|
||||
|
||||
検索が「人かどうかの確認(CAPTCHA)」で**完全に塞がれた**ときに、人が確認を済ませるまで作業を止めて待つためのツール(issue #1199 段階 2)。
|
||||
|
||||
## いつ使うか
|
||||
|
||||
`WebSearch` がこの形で失敗したときだけ:
|
||||
|
||||
```
|
||||
WebSearch error: ブラウザ検索がすべて CAPTCHA で止められ、SearXNG も結果を返しませんでした。
|
||||
検索そのものが実行できていません。
|
||||
```
|
||||
|
||||
そのうえで、**検索なしではその依頼を達成できない**ときに呼ぶ。
|
||||
|
||||
呼ぶ前に確かめること:
|
||||
|
||||
- URL が分かっているなら `WebFetch` で直接開けば済まないか
|
||||
- すでに手元にある資料や `Read` で足りないか
|
||||
- 検索がそもそも本筋でない依頼ではないか(雑談・要約・書き換えなど)
|
||||
|
||||
止まっているあいだ、利用者は確認画面を開いて操作する必要がある。**避けられるなら避けたい手間**なので、他に道が無いときだけ呼ぶこと。
|
||||
|
||||
## 引数
|
||||
|
||||
| 引数 | 必須 | 説明 |
|
||||
|------|------|------|
|
||||
| `reason` | いいえ | 検索が通らないと進められない理由。カードを見る人が読む |
|
||||
|
||||
## 呼んだあと何が起きるか
|
||||
|
||||
1. その movement が畳まれ、ジョブが「人の確認待ち」で**停車**する(ワーカーは解放される)
|
||||
2. 確認カードが出るのは**元のタスクのチャット**。サブタスクの中で呼んだ場合も、親をたどった先の元のタスクへ出る(#1225)
|
||||
3. 誰かが確認を済ませて「解決した」を押すと、**その確認画面で止まっているジョブがまとめて再開**する
|
||||
4. 「スキップ」を押した場合は、押したタスクのジョブと**その配下で確認待ちのサブタスク**だけが、検索が使えないまま再開する
|
||||
5. 再開すると同じステップの頭からやり直しになるので、もう一度 `WebSearch` を呼ぶこと
|
||||
|
||||
## 止まらないことがある
|
||||
|
||||
次のときは停車せず、エラーとして「続けてください」が返る。**呼べば必ず止まるツールではない**。
|
||||
|
||||
| 返る内容 | 意味 |
|
||||
|---|---|
|
||||
| この実行では人の確認を待てません | 親をたどっても元のタスクに行き着かない実行(チャットに紐づかない実行)。確認カードを出す先が無いので、止めても押す相手が居ない。検索抜きで組み立てるか `complete` で中断する |
|
||||
| まだ検索が塞がっていません | この実行の `WebSearch` が上のエラーで失敗していない。SearXNG 経由で結果が出ているなら塞がっていない |
|
||||
| 未解決の求めは残っていません | 確認画面に立っていた旗がもう下りている(誰かが解いた・画面が無い)。もう一度 `WebSearch` を試す |
|
||||
|
||||
待てるかどうかの判断は「サブタスクかどうか」では決まらない(#1225)。サブタスクでも、親をたどって元のタスクに行き着くなら待てる。行き着かない実行だけが上の 1 行目に当たる。
|
||||
|
||||
確認画面はワークスペース単位で共有される。同じワークスペースの誰かが一度解けば、そのあとの検索は通る。
|
||||
|
||||
## 関連
|
||||
|
||||
- 検索そのものは [WebSearch](websearch.md)
|
||||
- 停車と再開の仕組み(sentinel)は [movement 制御フローツール](../movement-control-flow-tools.md)
|
||||
@@ -27,7 +27,8 @@
|
||||
|
||||
| 名前 | 必須 | 説明 |
|
||||
|------|------|------|
|
||||
| `query` | ○(`around_ref` と排他) | 検索キーワード |
|
||||
| `query` | ○(`around_ref` と排他) | 検索キーワード。1 語または少数の語を推奨 |
|
||||
| `match_mode` | | 複数語の一致方法。`any`(既定)はいずれかの語、`all` はすべての語を含む結果に限定 |
|
||||
| `around_ref` | ○(`query` と排他) | `"comment:<id>"` 形式。指定した comment の前後を読む |
|
||||
| `context` | | 各ヒットに付す前後コメント件数(grep -C 相当)。既定 0、最大 5 |
|
||||
| `limit` | | 最大ヒット件数。既定 10、最大 30(`query` モードのみ) |
|
||||
@@ -41,7 +42,9 @@
|
||||
|
||||
日本語も含めた部分一致検索を trigram tokenize の FTS5 インデックスで行う。**3 文字未満の語は trigram で MATCH できないため、そのような語が含まれるクエリは自動的に LIKE(部分一致)にフォールバックする**(挙動は同じだが遅い)。
|
||||
|
||||
**複数語はスペース区切りの暗黙 AND(Google 的)**。`ログイン 認証` と入力すると「ログイン」と「認証」を**両方含む**コメントにヒットする(語順・位置は問わない)。フレーズ一致ではないので、離れて出現していても両語があればヒットする。各語は内部で個別に引用してから AND で結合するため、`AND`/`OR`/`NEAR`/`*`/`"` などの記号が語に混ざっても FTS5 演算子としては解釈されない(=ユーザー側から任意の演算子検索や `OR` 検索を差し込むことはできない)。広く探したいときは 1 語で、絞りたいときは語を足す。
|
||||
1 語または少数のキーワードで検索する。複数語の既定は `match_mode: "any"` で、いずれかの語を含むコメントを対象に、FTS5 の関連度順で返す。`ログイン 認証` なら「ログイン」だけ、または「認証」だけを含むコメントも候補になり、両方を強く含むコメントほど上位になりやすい。すべての語を含むコメントだけに絞る場合は `match_mode: "all"` を指定する(語順・位置は問わない)。各語は内部で個別に引用してから OR または AND で結合するため、`AND`/`OR`/`NEAR`/`*`/`"` などが語に混ざっても FTS5 演算子としては解釈されない。
|
||||
|
||||
短い語を含むため LIKE にフォールバックした場合も、`any` は OR、`all` は AND として一致する。LIKE 経路では関連度を計算できないため、結果は新しいコメント順になる。
|
||||
|
||||
SQLite のビルドが FTS5 に対応していない環境では、検索機能自体が使えない旨のメッセージを返す(エラーにはしない)。
|
||||
|
||||
@@ -85,3 +88,16 @@ SQLite のビルドが FTS5 に対応していない環境では、検索機能
|
||||
## 関連ツール
|
||||
|
||||
- `SearchTaskConversation` / `ReadTaskConversation` — 現在タスク自身の会話(コメント+transcript)を検索・閲覧する。他タスクへは到達できない。こちらは同じスペースの他タスクを横断検索する対
|
||||
|
||||
## ListWorkspaceTasks(一覧の相棒ツール)
|
||||
|
||||
内容の検索ではなく「同じワークスペースにどんなタスクがあるか」を俯瞰したいときは `ListWorkspaceTasks` を使う。
|
||||
|
||||
- `ListWorkspaceTasks({})` — 他タスクを新しい順に一覧(ID・状態・タイトル・piece・最終更新)。既定 20 件、`limit` で最大 50 件
|
||||
- `ListWorkspaceTasks({ state: "open" })` — 進行中のみ / `"closed"` — 完了のみ
|
||||
- スコープは SearchWorkspaceTasks と同一(同じスペースのみ・現在のタスクは除外)
|
||||
- 一覧で目星をつけたら `SearchWorkspaceTasks({ query: "...", task_id: <id> })` で中身を検索する使い方が典型
|
||||
|
||||
## 0 件時の複合句自動分割(2026-07-24)
|
||||
|
||||
スペースを含まない日本語の複合句(例: `満月の周期の計算`)は 1 語=フレーズ完全一致として扱われ、構成語が索引にあっても 0 件になることがある。`match_mode: "any"`(既定)で 0 件だった場合、助詞・句読点で分割した語群(例: `満月 周期 計算`)による再検索が自動で 1 回走り、結果の先頭に分割再検索した旨の注記が付く。最初からスペース区切りの単語で query を書けばこのフォールバックは不要。
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
> **デフォルト無効(opt-in)**。SpawnSubTask は Bash などと同じセンシティブ扱いのツールで、初期状態ではエージェントに提示されない。使うにはワークスペースの **設定 → ツール** タブで明示的に有効化する。有効化していない場合、SpawnSubTask は呼べず、分解は [`Delegate`](./delegate.md)(直列・インプロセス)で行う。
|
||||
>
|
||||
> タスク分解の既定手段は Delegate(直列)。research / general / brainstorming などのピースは Delegate でサブ調査を 1 件ずつこなす。SpawnSubTask は「壁時計時間の短縮のために独立テーマを本当に並列で走らせたい」ワークスペースだけが有効化する。
|
||||
> タスク分解の既定手段は Delegate(直列)。research / general などのピースは Delegate でサブ調査を 1 件ずつこなす。SpawnSubTask は「壁時計時間の短縮のために独立テーマを本当に並列で走らせたい」ワークスペースだけが有効化する。
|
||||
|
||||
## 基本
|
||||
|
||||
@@ -16,7 +16,7 @@ SpawnSubTask({
|
||||
})
|
||||
```
|
||||
|
||||
呼び出すと `subtasks/{index}/` にサブタスクのワークスペースが作られ、結果はそこに集約される。
|
||||
呼び出すと `subtasks/{連番}-{ジョブID先頭8桁}/`(例: `subtasks/1-9f0c1a2b/`)にサブタスクのワークスペースが作られ、結果はそこに集約される。ワークスペースはスペース内の全タスクで共有されるため、連番だけでは別タスクのサブタスクと同じディレクトリになってしまう。ジョブ ID を含めることで踏み合いを防いでいる。
|
||||
|
||||
## いつ使うか
|
||||
|
||||
@@ -63,18 +63,19 @@ SpawnSubTask({ title: "A の調査", instruction: "..." })
|
||||
SpawnSubTask({ title: "B の調査", instruction: "..." })
|
||||
SpawnSubTask({ title: "C の調査", instruction: "..." })
|
||||
WaitSubTask() // ここで全部の完了を待つ
|
||||
// 再開後、subtasks/*/result.md を Read して集約 → complete
|
||||
// 再開後、自分が起動したサブタスクの result.md を Read して集約 → complete
|
||||
```
|
||||
|
||||
- `WaitSubTask` は **同期的にブロックしない**。呼ぶとジョブはいったん停止(park)して worker を解放し、全サブタスクが終わると **同じ movement のまま再開** する
|
||||
- 再開時、各サブタスクの結果は `subtasks/{index}/result.md` に書き出されている。Read で読んで集約レポートを作る
|
||||
- 起動した分の成果物は `subtasks/{index}/output/` 以下にも残る
|
||||
- 再開時、各サブタスクの結果は そのサブタスクのワークスペース直下の `result.md` に書き出されている。再開時のシステム通知が自分のサブタスクのパスを列挙するので、そこに挙がったものだけを Read して集約レポートを作る
|
||||
- 起動した分の成果物は 同じワークスペースの `output/` 以下にも残る
|
||||
- `subtasks/` 直下には**別タスクのサブタスクも並ぶ**。通知に挙がっていないディレクトリは自分のものではないので読まない
|
||||
|
||||
### ワークフロー
|
||||
|
||||
1. `SpawnSubTask` を必要な数だけ呼ぶ(並列に起動される)
|
||||
2. `WaitSubTask()` を 1 回呼ぶ
|
||||
3. 再開後、`subtasks/*/result.md` を読んで集約する
|
||||
3. 再開後、通知に挙がった result.md を読んで集約する
|
||||
4. `complete` で終了する
|
||||
|
||||
### auto-park(取りこぼし防止)
|
||||
@@ -112,8 +113,9 @@ subtasks:
|
||||
## 結果の参照
|
||||
|
||||
サブタスク完了後、親タスクは:
|
||||
- `subtasks/{index}/result.md` にサブタスクの要約結果がある(Read で読む)
|
||||
- `subtasks/{index}/output/` 以下にサブタスクの成果物がある
|
||||
- 各サブタスクのワークスペース(`subtasks/{連番}-{ジョブID先頭8桁}/`)直下の `result.md` に要約結果がある(Read で読む)
|
||||
- 同じワークスペースの `output/` 以下に成果物がある
|
||||
- どのディレクトリが自分のものかは、再開時のシステム通知が列挙する
|
||||
|
||||
## 制限
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# Speak
|
||||
|
||||
文章を読み上げて音声ファイルにする。外部の読み上げサーバー(OpenAI 互換の `/audio/speech`)に文字を送り、返ってきた音声を workspace の `output/` に保存する。
|
||||
|
||||
## 基本
|
||||
|
||||
```js
|
||||
Speak({
|
||||
text: "おはようございます。今日の予定は 3 件です。",
|
||||
file_name: "morning-brief", // 省略時 speak-{日時}.{形式}
|
||||
voice: "jf_alpha" // 省略時 config の speak_voice
|
||||
})
|
||||
```
|
||||
|
||||
返り値は保存したパス。そのまま利用者に示せる。
|
||||
|
||||
```
|
||||
音声を保存しました -> output/morning-brief.mp3 (mp3, 48213 bytes, 24 文字, voice: jf_alpha)
|
||||
```
|
||||
|
||||
## サーバー設定(必須)
|
||||
|
||||
設定 → ツール → Speech の「読み上げ」の欄で設定する。**文字起こし(TranscribeAudio)とは別の接続先**で、片方だけ設定しても構わない。
|
||||
|
||||
| 欄 | 設定キー | 既定 |
|
||||
|---|---|---|
|
||||
| 読み上げの接続先 | `speak_server_url` | なし(未設定なら使えない) |
|
||||
| 読み上げモデル | `speak_model` | なし(送らない) |
|
||||
| 声 | `speak_voice` | なし(送らない) |
|
||||
| 出力の形式 | `speak_format` | `mp3` |
|
||||
| 読み上げのタイムアウト | `speak_timeout` | 120 秒 |
|
||||
| 文字数の上限 | `speak_max_chars` | 4096 文字 |
|
||||
|
||||
接続先が未設定のときは、送信せずに断る。
|
||||
|
||||
## ツールの入り切り
|
||||
|
||||
読み上げは分類 `speak` として、ワークスペースごとに入り切りを決める(設定 → ツール)。**既定は切**。接続先を設定していない状態では動かないため。
|
||||
|
||||
## 声の名前について
|
||||
|
||||
声の名前は**自由入力**で、こちらは選択肢を持たない。OpenAI 互換の仕様が声の語彙を定めておらず、サーバーの実装ごとに違うため。決め打ちすると、接続先を替えた瞬間に通らなくなる。
|
||||
|
||||
名前が通らなかったときは、サーバーが返した文をそのまま返す。
|
||||
|
||||
```
|
||||
読み上げサーバーエラー (400): {"error":{"message":"voice 'alloy' is not available"}}
|
||||
```
|
||||
|
||||
この文が「声の名前が違う」と気づける唯一の手がかりなので、加工しない。
|
||||
|
||||
## 出力の形式
|
||||
|
||||
仕様上は `mp3` / `opus` / `aac` / `flac` / `wav` / `pcm` があるが、**全部を持つサーバーとは限らない**。既定の `mp3` がいちばん通りやすい。指定した形式がそのまま拡張子になる。
|
||||
|
||||
`pcm` は画面のプレビューでは再生できない(ダウンロードはできる)。アプリの中で聴くなら `mp3` などにする。
|
||||
|
||||
## 文字数の上限
|
||||
|
||||
上限を超える文章は**断る。黙って切り詰めることはしない**。切ると、途中で終わった音声が「できた」として返り、最後まで聴いて初めて気づくことになる。
|
||||
|
||||
```
|
||||
文字数が上限を超えています(5200 文字 / 上限 4096 文字)。切り詰めてはいません。…
|
||||
```
|
||||
|
||||
長い文章は段落ごとに分けて複数回呼ぶか、設定で上限を上げる。
|
||||
|
||||
## 立てられるサーバーの例
|
||||
|
||||
OpenAI 互換の `/audio/speech` を持つ実装であれば何でもよい。
|
||||
|
||||
- [openedai-speech](https://github.com/matatonic/openedai-speech)
|
||||
- [openai-edge-tts](https://github.com/travisvn/openai-edge-tts)
|
||||
|
||||
音の品質・話速・言語の対応はサーバー側の責務で、このアプリでは扱わない。
|
||||
|
||||
## 関連
|
||||
|
||||
- 文字起こしは [TranscribeAudio](transcribeaudio.md)(逆向き。音声 → 文字)
|
||||
- 保存した音声はファイル窓のプレビューでそのまま再生できる
|
||||
@@ -0,0 +1,83 @@
|
||||
# SpinOffTask
|
||||
|
||||
いまの話とは別に取っておきたい話題を、**新しいチャット**として立てる。立てたチャットは独立して走り、結果はここへ戻ってこない。いま作業しているワークスペースへはそのまま立ち、**別のワークスペースへ立てるときは人の承認を待つ**。
|
||||
|
||||
> **デフォルト無効(opt-in)**。Bash や SpawnSubTask と同じセンシティブ扱いのツールで、初期状態ではエージェントに提示されない。使うにはワークスペースの **設定 → ツール** タブで明示的に有効化する。
|
||||
|
||||
## SpawnSubTask との違い(ここを取り違えない)
|
||||
|
||||
一文でいうと、**サブタスク起動はいまの作業を速く終わらせるための分割、切り出しはいまの作業とは別に取っておくための保存**。前者は親の文脈に戻ってくるが、後者は戻ってこない。
|
||||
|
||||
| 観点 | SpawnSubTask(サブタスク起動) | SpinOffTask(切り出し) |
|
||||
|---|---|---|
|
||||
| 作られるもの | ジョブだけ。チャットは作られない | チャット+ジョブ |
|
||||
| 一覧に出るか | 出ない。親タスクのサブタスクタブからしか見えない | 出る。ワークスペースのチャット一覧に並ぶ |
|
||||
| 実行先 | 親の作業ディレクトリ配下の隔離ディレクトリ | ワークスペースの共有ツリー |
|
||||
| 親との関係 | 親が完了を待って停車し、結果を読んで統合する | 親は待たない。独立して走る |
|
||||
| 上限 | 入れ子の深さ上限と親あたりの本数上限がある | 1 回の実行で 3 件まで(承認待ちのカードも数える)。切り出しで立ったチャットからは切り出せない |
|
||||
| 会話・タイトル | 持たない | 持つ。あとから続きを話せる |
|
||||
|
||||
**判断のしかた**——その結果がいまの回答に必要なら SpawnSubTask(または `delegate`)。必要ないが取っておきたいなら SpinOffTask。
|
||||
|
||||
## 基本
|
||||
|
||||
```js
|
||||
SpinOffTask({
|
||||
title: "ローカル LLM の量子化方式を掘り下げる",
|
||||
instruction: "GGUF の量子化方式(Q4_K_M / Q5_K_M / Q8_0)の精度と速度の違いを調べて、選び方の目安を output/quantization.md にまとめる。",
|
||||
excerpt: "…(引用したい元の文章)…", // 任意
|
||||
source: "ファイル output/report.md", // 任意
|
||||
piece: "research", // 任意。省略すると chat
|
||||
spaceId: "sp-xxxx" // 任意。別のワークスペースへ立てるときだけ(承認待ちになる)
|
||||
})
|
||||
```
|
||||
|
||||
## いつ使うか
|
||||
|
||||
- 会話の途中で別の話題が立ち上がったが、**いまの回答には要らない**とき
|
||||
- 利用者が「それは別で調べておいて」と言ったとき
|
||||
- 読んでいた資料の一部を、あとで別途扱いたいとき
|
||||
|
||||
呼んだあとは待たない。結果は戻ってこないので、そのままいまの作業を続ける。
|
||||
|
||||
## 引数
|
||||
|
||||
| 引数 | 必須 | 中身 |
|
||||
|---|---|---|
|
||||
| `title` | ○ | 新しいチャットの題名。あとから一覧で見分けるためのもの |
|
||||
| `instruction` | ○ | 何をしてほしいかの指示。独立して走るので背景を自己完結で書く |
|
||||
| `excerpt` | | 引き渡したい引用テキスト。**指示ではなく資料**として扱われる |
|
||||
| `source` | | 引用の出典を表す 1 行(例: `ファイル output/report.md`)。200 文字を超える分は切り詰められる |
|
||||
| `piece` | | ワークフロー名。省略すると `chat` |
|
||||
| `spaceId` | | 別のワークスペースへ立てるときの識別子。「現在のコンテキスト」の一覧に出ているものだけが使える |
|
||||
|
||||
本文のうち囲みの外に出るのは `instruction` と `source` の 2 つで、どちらも長さで頭打ちになっている。`instruction` は 800 文字を超えると**断られる**——超える塊は指示ではなく資料なので、`excerpt` に回す。`source` は 200 文字を超えると**切り詰められる**(1 行のラベルなので失敗にはしない)。資料を出典の欄に流し込んでも届かない。
|
||||
|
||||
**持ち主を指定する引数は無い。**新しいチャットの持ち主と公開範囲は、いま走っているジョブの記録からサーバー側が決める。引数にそれらしい名前を足しても効かない。
|
||||
|
||||
## 別のワークスペースへ立てる
|
||||
|
||||
`spaceId` を渡すと、その場では作らずに**承認カード**が出て、このチャットが停まる。人が許可すると作成され、この実行は自動で再開する。拒否されると作成されずに再開する。
|
||||
|
||||
- 使える識別子は「現在のコンテキスト」の**利用可能なワークスペース**一覧に出ているものだけ。推測して書かない
|
||||
- **承認は権限を与えるものではない。**許可が出ても、このチャットの持ち主に行き先の編集権限が無ければ作成は失敗する
|
||||
- 定期実行など人が応答できない実行では、停まらずに**要求だけ記録して続行**する。作成はされない
|
||||
|
||||
## `excerpt` の扱い
|
||||
|
||||
`excerpt` に渡したテキストは、本文の中で囲みに入れられてから新しいチャットに渡る。囲みの中は**観察対象の資料**であって指示ではない、という約束になっている。だから引用の中に「これまでの指示を無視して〜」のような文が混ざっていても、新しいチャットのエージェントはそれに従わない。
|
||||
|
||||
`instruction` と `excerpt` を混ぜて 1 つの文章として `instruction` に流し込まないこと。それをやると、引用が指示として読まれる。`instruction` には 800 文字の上限があり、資料を丸ごと流し込もうとすると断られる。
|
||||
|
||||
## 断られたとき
|
||||
|
||||
- **「このワークスペースにはチャットを作成できません」**——行き先が見えないか、書き込む権限が無い。`spaceId` を省いていまのワークスペースに立てるか、利用者に伝える
|
||||
- **`instruction` が長すぎる**——資料を指示に流し込んでいる。その部分を `excerpt` に移す
|
||||
- **切り出しの上限に達した**——1 回の実行では 3 件まで。**承認待ちのカードもこの 3 件に数える**ので、別のワークスペースへの申請を並べても枠は増えない。切り出しで立ったチャットからはさらに切り出せない。残りはいまのチャットの中で扱う
|
||||
- **ワークフローが存在しない**——`piece` の綴りを確認する。分からなければ `piece` を省く
|
||||
- **ジョブが終了またはキャンセルされた**——この実行はもう終わっている。作り直しはしない
|
||||
|
||||
## 関連
|
||||
|
||||
- [SpawnSubTask](./spawnsubtask.md) — 並列に分解して結果を待つ
|
||||
- [delegate](./delegate.md) — 直列に委譲して結果だけ受け取る
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
AI と人間が共有する SSH PTY セッションを操作する 3 ツール。**1 タスクは接続ごとに 1 つ、複数の PTY セッションを同時に持てる**(例: 2 台のサーバーへ同時接続して並行作業)。各セッション内では `cd` / 環境変数 / foreground プロセスが job をまたいで維持される。長時間の対話作業 / TUI (vim, top, less, tmux) / 複数ラウンドの調査向け。
|
||||
|
||||
単発コマンドだけなら **`SshExec`** (ssh-ops piece) のほうが軽い。本ツール群は対話的シェル + AI が画面を見続ける用途に最適化されている。
|
||||
単発コマンドだけなら **`SshExec`** を直接呼ぶほうが軽い。本ツール群は対話的シェル + AI が画面を見続ける用途に最適化されている。
|
||||
|
||||
> **ユーザーが先にセッションを開いている場合がある**: タスク詳細の **Console タブ**から、ユーザーがタブを追加して自分でセッションを起動できる(複数タブを同時に開ける。→[SSH 接続](../../ui/src/content/help/14-ssh.md))。その場合 `SshConsoleEnsure` は既存セッションをそのまま再利用する。「まず console を開く」操作を AI 側でやり直す必要はない。ただし**タスクに複数セッションが同時に開いている場合、`connection_id` を省略して自動採用されるのは「直前にこのツール群で操作した接続 (current-connection pointer)」か「セッションが 1 つしかない」ときだけ**。2 つ以上開いていて pointer も無い状態で省略すると `ambiguous` エラーになる(詳細は後述)。
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ WebFetch({ url: "https://example.com/article", timeout: 30 })
|
||||
- JSON / XML / プレーンテキストもそのまま取得可能
|
||||
- リダイレクトは自動で追従
|
||||
|
||||
返却テキストは context 節約のため約 10,000 文字で切り詰められる。**スペースタスクでは全文(未切り詰め)が `source/` に保存される**ので、切り詰めが起きると末尾に `... (truncated; full version saved to source/<file>)` という案内が付く。全文を読みたいときは保存された `source/` のファイルを `Read` で開けばよい。
|
||||
返却テキストは context 節約のため約 10,000 文字で切り詰められる。**全文(未切り詰め)は `source/` に保存される**ので、切り詰めが起きると末尾に `... (truncated; full version saved to source/<file>)` という案内が付く。全文を読みたいときは保存された `source/` のファイルを `Read` で開けばよい。
|
||||
|
||||
## いつ使うか
|
||||
|
||||
@@ -43,7 +43,7 @@ WebFetch は軽量で速い。BrowseWeb はブラウザ起動コストがかか
|
||||
### 全文は source へ、切り詰め版は agent へ
|
||||
|
||||
- agent のコンテキストに返るのは約 10,000 文字に切り詰めた版(context 膨張を防ぐため)
|
||||
- **スペースタスクでは、切り詰める前の全文クリーン Markdown が `source/` に保存される**
|
||||
- **切り詰める前の全文クリーン Markdown が `source/` に保存される**(タスクがワークスペースに属していなくても同じ)
|
||||
- 切り詰めが起きた場合、返却テキスト末尾に `... (truncated; full version saved to source/<file>)` が付く
|
||||
- 病的に巨大なページ対策として、`source/` 保存も 2MB で打ち切る(その旨ノートを付す)
|
||||
|
||||
@@ -99,12 +99,13 @@ WebFetch は取得 body の先頭 8KB を sniff し(magic byte / NUL / 不正
|
||||
`DownloadFile` で `input/` に保存し、`ReadExcel` / `ReadPdf` 等を使う。テキスト body は
|
||||
5MB で打ち切られる(末尾に `[truncated: body exceeded 5MB]`)。
|
||||
|
||||
## ソースライブラリへの蓄積(スペースタスク)
|
||||
## ソースライブラリへの蓄積
|
||||
|
||||
スペースのタスクで WebFetch がページ本文を取得すると、**クリーン抽出した全文 Markdown**(agent への
|
||||
WebFetch がページ本文を取得すると、**クリーン抽出した全文 Markdown**(agent への
|
||||
返却で切り詰める前のもの)が `source/` に保存され、出典メタ(URL・タイトル・取得日時・サイズ)が
|
||||
`source/index.jsonl` に追記される。
|
||||
これにより「そのスペースのソース(資料)」が自動で溜まり、後から追加調査やグラウンディングに使える。
|
||||
同一 URL は重複保存しない(既に index にあればスキップ)。スペース文脈でないタスク
|
||||
(ephemeral / gitea issue 等)では従来どおり蓄積しない。`logs/raw/` への生データ保存は従来どおり。
|
||||
これにより「そのタスクのソース(資料)」が自動で溜まり、後から追加調査やグラウンディングに使える。
|
||||
同一 URL は重複保存しない(既に index にあればスキップ)。
|
||||
**属している場所で挙動は変えない**——スペースのタスクでも、タスク一覧から直接作ったタスクでも、
|
||||
定期実行・サブタスクでも同じように溜まる(issue #1097 / 由来 #1093)。`logs/raw/` への生データ保存は従来どおり。
|
||||
BrowseWeb(ブラウザ閲覧)や DownloadFile(`section: "source"`)も同じ `source/index.jsonl` に合流する。
|
||||
|
||||
@@ -82,6 +82,14 @@ await call({ type: 'deleteFile', path: 'output/old.txt' }); // 削
|
||||
|
||||
`apps/{name}/app.json`(任意)で一覧の表示名・説明・エントリを指定できる(無ければフォルダ名)。
|
||||
|
||||
利用者が「アプリ」タブの「アプリを追加」で手元の HTML を持ち込んだアプリも同じ `apps/` に並ぶ。
|
||||
その場合 `app.json` は追加時に `{ title, description: "", entry: "index.html" }` の形で自動生成される
|
||||
(`title` は HTML の `<title>`、無ければフォルダ名)。手で置いたアプリには `app.json` が無いこともあるので、
|
||||
一覧・起動のどちらも `app.json` の有無に依存しないこと。持ち込みは自動 E2E テストを通らないため、
|
||||
ブラウザ内の保存(localStorage 等)・ダウンロード・ファイル選択・新窓に頼る HTML はサンドボックスで
|
||||
そのままでは動かない。持ち込まれたアプリの修正を頼まれたら、ブラウザ内の保存は上のブリッジ API
|
||||
(`apps/{name}/data/` への `writeFile` / `readFile`)に置き換える。
|
||||
|
||||
## E2E テスト(verify ステップ)
|
||||
|
||||
`workspace-app` ピースの verify ステップは、作成したアプリを **`TestWorkspaceApp` ツールで自動 E2E テスト** する。
|
||||
|
||||
@@ -199,6 +199,13 @@ text-only tweet には呼ばないこと (14 秒を無駄にする)。
|
||||
|
||||
## トラブルシューティング
|
||||
|
||||
- **エラーは出ないのに常に 0 件**: X がウェブバンドル構成を変え、リクエスト署名
|
||||
(`x-client-transaction-id`) を作れなくなった状態。署名なしのリクエストに X は
|
||||
黙って空を返すため、認証切れとは症状が異なる。アダプタは
|
||||
`scripts/x-adapter/x_adapter.py` の `find_indices_url_deep` で署名材料
|
||||
(`sign.o-*.js`) を探しており、X 側の構成変更で再発しうる。再発時はこの関数の
|
||||
探索段数を実際のバンドル構造に合わせて調整する。現在はこの状態を検知したら
|
||||
空ではなく明示エラーで落とす (2026-07-21 / 上流 twscrape issue #320)
|
||||
- **認証エラー**: cookie の有効期限切れ。ブラウザで取得し直して Settings に再投入
|
||||
- **rate limit**: しばらく待ってから再実行。検索回数を絞る
|
||||
- **twitter-cli not found**: `scripts/install-twitter-cli.sh` で導入が必要
|
||||
|
||||
Reference in New Issue
Block a user