@falkenslab/agent-kit 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/knowledge-plugin/.claude-plugin/plugin.json +1 -1
- package/assets/knowledge-plugin/commands/ingest.md +1 -1
- package/assets/knowledge-plugin/commands/lint.md +1 -1
- package/assets/knowledge-plugin/skills/knowledge-ingest/SKILL.md +13 -19
- package/assets/knowledge-plugin/skills/knowledge-lint/SKILL.md +18 -14
- package/assets/knowledge-plugin/skills/knowledge-query/SKILL.md +5 -5
- package/assets/knowledge-plugin-files/.claude-plugin/plugin.json +4 -0
- package/assets/knowledge-plugin-files/commands/ingest.md +7 -0
- package/assets/knowledge-plugin-files/commands/lint.md +5 -0
- package/assets/knowledge-plugin-files/commands/query.md +5 -0
- package/assets/knowledge-plugin-files/skills/knowledge-ingest/SKILL.md +46 -0
- package/assets/knowledge-plugin-files/skills/knowledge-lint/SKILL.md +26 -0
- package/assets/knowledge-plugin-files/skills/knowledge-query/SKILL.md +14 -0
- package/dist/core/agentSpec.d.ts +27 -2
- package/dist/core/extractText.d.ts +13 -0
- package/dist/core/extractText.js +147 -0
- package/dist/core/fileKnowledgeStore.d.ts +10 -0
- package/dist/core/fileKnowledgeStore.js +378 -0
- package/dist/core/hooks/fileScopeGate.d.ts +8 -0
- package/dist/core/hooks/fileScopeGate.js +20 -0
- package/dist/core/hooks/humanInput.d.ts +18 -0
- package/dist/core/hooks/humanInput.js +30 -2
- package/dist/core/hooks/planGate.js +14 -5
- package/dist/core/hooks/stepGate.js +6 -0
- package/dist/core/interaction.d.ts +20 -0
- package/dist/core/knowledge.d.ts +14 -4
- package/dist/core/knowledge.js +36 -6
- package/dist/core/knowledgeStore.d.ts +134 -0
- package/dist/core/knowledgeStore.js +84 -0
- package/dist/core/messages/de.js +64 -0
- package/dist/core/messages/en.d.ts +39 -0
- package/dist/core/messages/en.js +64 -0
- package/dist/core/messages/es.js +64 -0
- package/dist/core/messages/fr.js +64 -0
- package/dist/core/modeControl.d.ts +36 -0
- package/dist/core/modeControl.js +61 -0
- package/dist/core/session.d.ts +6 -31
- package/dist/core/session.js +76 -71
- package/dist/core/sources.d.ts +122 -0
- package/dist/core/sources.js +312 -0
- package/dist/core/todos.d.ts +23 -0
- package/dist/core/todos.js +33 -0
- package/dist/core/toolLabels.d.ts +1 -1
- package/dist/core/toolLabels.js +42 -2
- package/dist/core/tools/humanApproval.d.ts +9 -5
- package/dist/core/tools/humanApproval.js +39 -7
- package/dist/core/tools/knowledgeTools.d.ts +16 -0
- package/dist/core/tools/knowledgeTools.js +161 -0
- package/dist/core/tools/saveToSources.d.ts +27 -8
- package/dist/core/tools/saveToSources.js +203 -55
- package/dist/core/tools/time.d.ts +29 -0
- package/dist/core/tools/time.js +129 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -1
- package/dist/tui/chatTui.js +14 -1
- package/dist/tui/consoleRenderer.js +16 -2
- package/dist/tui/ink/SessionView.js +67 -17
- package/dist/tui/ink/inkInteraction.d.ts +4 -2
- package/dist/tui/ink/inkInteraction.js +20 -2
- package/dist/tui/ink/runChatInk.js +5 -0
- package/dist/tui/ink/sessionModel.d.ts +3 -0
- package/dist/tui/ink/sessionModel.js +14 -1
- package/dist/tui/terminalInteraction.js +2 -0
- package/package.json +31 -1
- /package/assets/{knowledge-plugin → knowledge-plugin-files}/skills/knowledge-pages/SKILL.md +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "knowledge",
|
|
3
|
-
"description": "Built-in knowledge base workflows (an LLM wiki)
|
|
3
|
+
"description": "Built-in knowledge base workflows (an LLM wiki) over the kit's knowledge_* tools: ingesting sources, querying the knowledge base and health-checking it."
|
|
4
4
|
}
|
|
@@ -4,4 +4,4 @@ description: Ingest material into the knowledge base right now - the files or to
|
|
|
4
4
|
|
|
5
5
|
Apply the `knowledge-ingest` skill right now to: $ARGUMENTS
|
|
6
6
|
|
|
7
|
-
If nothing is given above,
|
|
7
|
+
If nothing is given above, ingest every original `list_sources` reports as new, and update the summaries of the ones it reports as changed. When you're done, summarize which pages you created and updated, and anything you couldn't read.
|
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
description: Health-check the knowledge base right now - broken links, orphans, duplicates, contradictions, gaps
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
Apply the `knowledge-lint` skill right now over the whole knowledge base. Fix what's mechanical (links,
|
|
5
|
+
Apply the `knowledge-lint` skill right now over the whole knowledge base. Fix what's mechanical (broken links, orphans, links to retired pages) and report the rest: what needs the human's decision, and the gaps worth filling next.
|
|
@@ -1,42 +1,36 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: knowledge-ingest
|
|
3
|
-
description: Ingest one source into the knowledge base -
|
|
3
|
+
description: Ingest one source into the knowledge base - an original in sources/, a document, a page - creating its summary page, creating or updating the concept and entity pages it touches, and logging it.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Ingesting a source into the knowledge base
|
|
7
7
|
|
|
8
|
-
A source is ingested once, and from then on the knowledge base holds what it taught: later sessions read the pages, not the original again.
|
|
8
|
+
A source is ingested once, and from then on the knowledge base holds what it taught: later sessions read the pages, not the original again. Its value is in how it connects to what the knowledge base already knows. Work only through the `knowledge_*` tools; links between pages are ids, `[Bowline](concept/bowline)`, and the index and backlinks are kept for you.
|
|
9
9
|
|
|
10
10
|
## 1. Check it isn't already ingested
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
- unchanged source: nothing to do;
|
|
14
|
-
- the source changed or the page is thin: update that page instead of creating another.
|
|
12
|
+
`list_sources` gives each original's status and summary pages: *new* needs ingesting; *ingested* has a page; *changed* changed after its ingest (update its summary). For a document from elsewhere, `knowledge_search` its title or URL.
|
|
15
13
|
|
|
16
14
|
## 2. Read it for real
|
|
17
15
|
|
|
18
|
-
Read the whole source, not its first page. If it exists only outside the sources folder (a
|
|
16
|
+
Read the whole source, not its first page (a long PDF in parts: `list_sources` gives its pages). Its path is `sources/<path>` in the project folder, not in this skill's folder. `Read` reads PDFs and images; `extract_text` reads DOCX, PPTX (with its speaker notes) and XLSX. If it exists only outside the sources folder, keep the original first: `download_to_sources` for a URL (a web page is kept as markdown too: read that one), `save_to_sources` for a file in the run's folder, `request_file` to ask the person for one.
|
|
19
17
|
|
|
20
18
|
## 3. Write the summary page
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
`knowledge_create` with type `summary` (call it once without content to get the template), and the field `file` (the original, relative to sources/) or `url`. Key points quote literally where exactness matters. Create the new concept and entity pages it feeds in the same call (`also`): they can link to each other.
|
|
23
21
|
|
|
24
22
|
## 4. Update concepts and entities
|
|
25
23
|
|
|
26
|
-
For every concept or entity the source explains or relies on:
|
|
27
|
-
- no page yet: create it;
|
|
28
|
-
- page exists: add what is new (a better definition, an example, a nuance
|
|
24
|
+
For every concept or entity the source explains or relies on, `knowledge_search` it first (under other names and aliases too: a duplicate page is worse than none):
|
|
25
|
+
- no page yet: create it, with the summary (`also`) or on its own;
|
|
26
|
+
- page exists: add what is new (a better definition, an example, a nuance) with `knowledge_edit`, and link the summary under its sources.
|
|
29
27
|
|
|
30
|
-
|
|
28
|
+
If the source contradicts a page, record both versions with attribution in both pages; don't overwrite.
|
|
31
29
|
|
|
32
|
-
## 5.
|
|
30
|
+
## 5. Close
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
`knowledge_log` with operation `ingest`, the source's title and the pages created and updated. Rewrite the `overview` (`knowledge_rewrite`) only if the source changes the big picture. When ingesting several sources in a row, log once at the end.
|
|
35
33
|
|
|
36
|
-
##
|
|
34
|
+
## An original that was wrong or replaced
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
- `log.md`: `## [date] ingest | <source title>` with the pages created and updated.
|
|
40
|
-
- `overview.md`: only if the source changes the big picture.
|
|
41
|
-
|
|
42
|
-
When ingesting several sources in a row, do steps 1-5 for each and close once at the end.
|
|
36
|
+
`retire_source` (the person approves it) moves it aside and marks its summary retired (wrong) or superseded (replaced). Then `knowledge_check` lists the passages that cite it: after a wrong one, correct or remove what it taught (`knowledge_edit`, or `knowledge_retire` for a page that was only about it); after a replaced one, ingest the new original and re-ground those passages on it.
|
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: knowledge-lint
|
|
3
|
-
description: Health-check the knowledge base - broken links, pages
|
|
3
|
+
description: Health-check the knowledge base - broken links, orphan pages, links to retired pages, missing or changed originals, duplicated concepts, mentioned-but-missing concepts, contradictions, gaps - fix what's mechanical and report the rest.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Linting the knowledge base
|
|
7
7
|
|
|
8
|
-
A knowledge base maintained across sessions drifts
|
|
8
|
+
A knowledge base maintained across sessions drifts. Lint it when asked and after a large ingest.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## 1. The mechanical checks, in one call
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
`knowledge_check` returns broken links, orphan pages, links to retired pages, summaries whose original is gone, originals that are new, changed or missing, and the passages that cite a retired original. The index and backlinks are kept by the store: there's nothing to fix there.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
- **Broken links**: fix them with `knowledge_edit` when the right target is obvious (a typo, a page under a close name: `knowledge_search`); otherwise report them.
|
|
15
|
+
- **Orphans**: link them from the pages where they belong, or report why they don't belong anywhere.
|
|
16
|
+
- **Links to retired pages, passages citing a retired original**: correct or re-ground them.
|
|
17
|
+
- **New or changed originals**: report them as what to ingest or update next (or do it, if asked).
|
|
18
|
+
|
|
19
|
+
## 2. The checks that need judgement
|
|
20
|
+
|
|
21
|
+
Use `knowledge_index` and `knowledge_search`; read only the pages a check needs.
|
|
22
|
+
|
|
23
|
+
- **Duplicates**: two pages for the same idea under different names. Merge the content into one with `knowledge_edit`, `knowledge_supersede` the other (never delete it) and repoint links.
|
|
24
|
+
- **Missing pages**: terms that several pages treat as concepts or entities but have no page. Create them if the material is there; otherwise list them.
|
|
25
|
+
- **Contradictions**: claims that disagree across pages without being recorded as such. Record both sides, attributed.
|
|
26
|
+
- **Provenance**: claims with no traceable source. Add the link, or mark them as unsourced.
|
|
23
27
|
|
|
24
28
|
## Close
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
`knowledge_log` with operation `lint` and what was fixed. Then report to the person: what you fixed, and what needs their decision or more material. Don't make judgement calls silently: merging pages that are only similar, or choosing between contradictory sources, goes in the report.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: knowledge-query
|
|
3
|
-
description: Answer a question from the knowledge base - find the relevant pages
|
|
3
|
+
description: Answer a question from the knowledge base - find the relevant pages, read them, answer citing them, and file the answer back as a synthesis page when it is worth keeping.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Querying the knowledge base
|
|
7
7
|
|
|
8
8
|
The knowledge base exists so a question doesn't start from zero. Answer from it first; go back to the originals or the web only for what it doesn't hold.
|
|
9
9
|
|
|
10
|
-
1. **
|
|
11
|
-
2. **Answer from the pages**,
|
|
10
|
+
1. **Find the pages.** `knowledge_search` the question's terms (and their synonyms), or `knowledge_index` for the catalog; `knowledge_read` only the pages that matter.
|
|
11
|
+
2. **Answer from the pages**, citing each one you drew on. Say what comes from the knowledge base, what from an original in the sources folder (read it if the knowledge base is thin or contradictory) and what from outside (label it as such).
|
|
12
12
|
3. **Say what is missing.** If the knowledge base can't answer part of the question, say so and name the source that would fill it, instead of filling the gap from memory silently.
|
|
13
|
-
4. **File it back.** If the answer is a comparison, an analysis or a report that will be asked again,
|
|
14
|
-
5. **Feed the knowledge base.** If answering revealed a concept
|
|
13
|
+
4. **File it back.** If the answer is a comparison, an analysis or a report that will be asked again, `knowledge_create` a `synthesis` page with links to its inputs, and `knowledge_log` it (operation `query`). A quick factual answer needs no page.
|
|
14
|
+
5. **Feed the knowledge base.** If answering revealed a concept with no page, or a contradiction, fix that too and log it.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Ingest material into the knowledge base right now - the files or topic given, or everything in sources/ not ingested yet
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Apply the `knowledge-ingest` skill right now to: $ARGUMENTS
|
|
6
|
+
|
|
7
|
+
If nothing is given above, ingest every original `list_sources` reports as new, and update the summaries of the ones it reports as changed. When you're done, summarize which pages you created and updated, and anything you couldn't read.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Health-check the knowledge base right now - broken links, orphans, duplicates, contradictions, gaps
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Apply the `knowledge-lint` skill right now over the whole knowledge base. Fix what's mechanical (links, index, missing back-links) and report the rest: what needs the human's decision, and the gaps worth filling next.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: knowledge-ingest
|
|
3
|
+
description: Ingest one source into the knowledge base - a file in sources/, a document, a page - creating its summary page, creating or updating the concept and entity pages it touches, linking everything both ways, and updating index.md and log.md.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Ingesting a source into the knowledge base
|
|
7
|
+
|
|
8
|
+
A source is ingested once, and from then on the knowledge base holds what it taught: later sessions read the pages, not the original again. Ingesting is not summarizing a file in isolation - its value is in how it connects to what the knowledge base already knows. Load the `knowledge-pages` skill first for the page templates.
|
|
9
|
+
|
|
10
|
+
## 1. Check it isn't already ingested
|
|
11
|
+
|
|
12
|
+
`list_sources` gives each original's status and summary pages: *new* needs ingesting; *ingested* has a page; *changed* changed after its ingest. For a document from elsewhere, look for it in `index.md` (Summaries) or `Grep` its URL inside the knowledge base. If it already has a page:
|
|
13
|
+
- unchanged source: nothing to do;
|
|
14
|
+
- the source changed or the page is thin: update that page instead of creating another.
|
|
15
|
+
|
|
16
|
+
## 2. Read it for real
|
|
17
|
+
|
|
18
|
+
Read the whole source, not its first page (a long PDF in parts: `list_sources` gives its pages). If it exists only outside the sources folder, keep the original first: `download_to_sources` for a URL (a web page is kept as markdown too: read that one), `save_to_sources` for a file in the run's folder, `request_file` to ask the person for one. `Read` reads PDFs and images; read DOCX, PPTX (with its speaker notes) and XLSX with `extract_text`.
|
|
19
|
+
|
|
20
|
+
## 3. Write the summary page
|
|
21
|
+
|
|
22
|
+
Create `summaries/<slug>.md` with the template: summary, key points (literal wording where exactness matters), frontmatter `file`/`url` pointing to the original.
|
|
23
|
+
|
|
24
|
+
## 4. Update concepts and entities
|
|
25
|
+
|
|
26
|
+
For every concept or entity the source explains or relies on:
|
|
27
|
+
- no page yet: create it;
|
|
28
|
+
- page exists: add what is new (a better definition, an example, a nuance, a fact) with `Edit`, and link the summary under "Sources".
|
|
29
|
+
|
|
30
|
+
Check first whether it exists under another name (`Grep` the knowledge base for the term and look at `aliases`): a duplicate page is worse than none.
|
|
31
|
+
|
|
32
|
+
## 5. Link both ways and look for conflicts
|
|
33
|
+
|
|
34
|
+
Every link you added from page A to page B needs its counterpart in B. If the source contradicts an existing page, record both versions with attribution in both pages - don't overwrite.
|
|
35
|
+
|
|
36
|
+
## 6. Close
|
|
37
|
+
|
|
38
|
+
- `index.md`: new pages, and one-line descriptions that changed.
|
|
39
|
+
- `log.md`: `## [date] ingest | <source title>` with the pages created and updated.
|
|
40
|
+
- `overview.md`: only if the source changes the big picture.
|
|
41
|
+
|
|
42
|
+
When ingesting several sources in a row, do steps 1-5 for each and close once at the end.
|
|
43
|
+
|
|
44
|
+
## An original that was wrong or replaced
|
|
45
|
+
|
|
46
|
+
`retire_source` (the person approves it) moves it aside and marks its summary retired (wrong) or superseded (replaced by a better one). Then update `index.md`, and fix the pages that cite it (`Grep` its file name): after a wrong one, correct or remove what it taught; after a replaced one, ingest the new original and re-ground those passages on it.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: knowledge-lint
|
|
3
|
+
description: Health-check the knowledge base - broken links, pages missing from index.md, orphan pages, one-way links, duplicated concepts, mentioned-but-missing concepts, contradictions, gaps - fix what's mechanical and report the rest.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Linting the knowledge base
|
|
7
|
+
|
|
8
|
+
A knowledge base maintained across sessions drifts: links break, pages go unlisted, the same concept appears under two names. Lint it when asked and after a large ingest. Load `knowledge-pages` first for the templates.
|
|
9
|
+
|
|
10
|
+
## Checks
|
|
11
|
+
|
|
12
|
+
Use `Glob` to list pages and `Grep` (always with `path` inside the knowledge base) to find links, instead of reading every page in full.
|
|
13
|
+
|
|
14
|
+
1. **Broken links**: a relative link whose target file doesn't exist. Fix it if the right target is obvious (a typo, a page under a close name); otherwise report it.
|
|
15
|
+
2. **Index**: every page is listed in `index.md` and every entry there points to an existing page. Fix it.
|
|
16
|
+
3. **Orphans**: pages nothing links to (`Grep` for the file name). Link them from where they belong, or report why they don't belong anywhere.
|
|
17
|
+
4. **One-way links**: a summary that feeds a concept which doesn't list it back... Add the missing side.
|
|
18
|
+
5. **Duplicates**: two pages for the same idea under different names. Merge the content into one, mark the other as superseded (never delete it) and repoint links.
|
|
19
|
+
6. **Missing pages**: terms that several pages treat as concepts or entities but have no page. Create them if the material is there; otherwise list them.
|
|
20
|
+
7. **Contradictions**: claims that disagree across pages without being recorded as such. Record both sides, attributed.
|
|
21
|
+
8. **Provenance**: claims with no traceable source. Add the link, or mark them as unsourced.
|
|
22
|
+
9. **Gaps**: originals `list_sources` reports as new (no summary yet), changed (their summary may be stale) or missing (deleted by hand: their summaries point nowhere), and concepts mentioned but never explained by any source. Report them - they are what to ingest, update or find next.
|
|
23
|
+
|
|
24
|
+
## Close
|
|
25
|
+
|
|
26
|
+
Append `## [date] lint | <summary>` to `log.md` listing what was fixed. Then report to the human: what you fixed, and what needs their decision or more material. Don't make judgement calls silently - merging pages that are only similar, or choosing between contradictory sources, goes in the report.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: knowledge-query
|
|
3
|
+
description: Answer a question from the knowledge base - find the relevant pages through the index, read them, answer with links to them, and file the answer back as a synthesis page when it is worth keeping.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Querying the knowledge base
|
|
7
|
+
|
|
8
|
+
The knowledge base exists so a question doesn't start from zero. Answer from it first; go back to the originals or the web only for what it doesn't hold.
|
|
9
|
+
|
|
10
|
+
1. **Start from the index.** Read `index.md`, pick the pages that can matter, read those (not the whole knowledge base). `Grep` inside the knowledge base for terms and aliases the index doesn't reveal.
|
|
11
|
+
2. **Answer from the pages**, linking each one you drew on. Say what comes from the knowledge base, what comes from an original in the sources folder (read it if the knowledge base is thin or contradictory) and what comes from outside (label it as such).
|
|
12
|
+
3. **Say what is missing.** If the knowledge base can't answer part of the question, say so and name the source that would fill it, instead of filling the gap from memory silently.
|
|
13
|
+
4. **File it back.** If the answer is a comparison, an analysis or a report that will be asked again, save it as `syntheses/<slug>.md` (template in `knowledge-pages`) with links to its inputs, add it to `index.md`, and log it: `## [date] query | <question>`. A quick factual answer needs no page.
|
|
14
|
+
5. **Feed the knowledge base.** If answering revealed a concept or fact that has no page, or a contradiction, fix that in the knowledge base too and log it.
|
package/dist/core/agentSpec.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AgentDefinition as SdkSubagentDefinition, McpServerConfig, SettingSource } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
import type { KnowledgeStore, PageType } from "./knowledgeStore.js";
|
|
2
3
|
/**
|
|
3
4
|
* The human-supervision spectrum every agent built on this kit shares, independent of
|
|
4
5
|
* domain: "interactive" pauses before every single action, "guided" only pauses before a
|
|
@@ -49,6 +50,11 @@ export interface BaseSessionConfig {
|
|
|
49
50
|
* language (see language.ts). Pass the same value to the chat, wizard or progress view.
|
|
50
51
|
*/
|
|
51
52
|
language?: string;
|
|
53
|
+
/**
|
|
54
|
+
* The time zone the date and time tools answer in (`current_time`, `date_math`), an IANA
|
|
55
|
+
* name such as "Europe/Madrid"; the system's if not given.
|
|
56
|
+
*/
|
|
57
|
+
timeZone?: string;
|
|
52
58
|
}
|
|
53
59
|
/**
|
|
54
60
|
* Everything actually *about the domain* that `buildSessionOptions()` needs but doesn't
|
|
@@ -104,12 +110,31 @@ export interface AgentSpec<TConfig extends BaseSessionConfig> {
|
|
|
104
110
|
/**
|
|
105
111
|
* The built-in knowledge base (see knowledge.ts): when `config.knowledgeDir` is set, the kit
|
|
106
112
|
* appends its "Knowledge base" rules to the system prompt and loads its plugin (skills
|
|
107
|
-
* knowledge-
|
|
108
|
-
* /knowledge:lint), so the agent maintains its notes as an interlinked wiki
|
|
113
|
+
* knowledge-ingest/knowledge-query/knowledge-lint, commands /knowledge:ingest, /knowledge:query,
|
|
114
|
+
* /knowledge:lint), so the agent maintains its notes as an interlinked wiki, through the
|
|
115
|
+
* `knowledge_*` tools by default (see `knowledgeTools`). On by default;
|
|
109
116
|
* set `false` for an agent that writes its own rules for `knowledgeDir` (or wants plain
|
|
110
117
|
* notes) — `knowledgePromptSection()`/`knowledgePluginRoot()` are exported to reuse the pieces.
|
|
111
118
|
*/
|
|
112
119
|
knowledgeBase?: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* How the agent reaches the built-in knowledge base: `"store"` (the default), only through
|
|
122
|
+
* the kit's `knowledge_*` tools over a `KnowledgeStore`, never with the file tools, so the
|
|
123
|
+
* storage can change and the wiki's rules are kept by code (ADR-024); `"files"`, with the
|
|
124
|
+
* file tools on `knowledgeDir` and the rules in the prompt, as before.
|
|
125
|
+
*/
|
|
126
|
+
knowledgeTools?: "store" | "files";
|
|
127
|
+
/**
|
|
128
|
+
* The agent's own page types for the built-in knowledge base, besides the kit's four
|
|
129
|
+
* (summary, concept, entity, synthesis): each with its folder (`""` for the root), index
|
|
130
|
+
* section, description (told to the model) and template.
|
|
131
|
+
*/
|
|
132
|
+
knowledgePageTypes?: PageType[];
|
|
133
|
+
/**
|
|
134
|
+
* The knowledge base's store, instead of the kit's over markdown files in `knowledgeDir`
|
|
135
|
+
* (e.g. a database or a vector store implementing `KnowledgeStore`).
|
|
136
|
+
*/
|
|
137
|
+
knowledgeStore?(config: TConfig): KnowledgeStore;
|
|
113
138
|
/**
|
|
114
139
|
* Whether the kit tells the agent (and each subagent) to reply in the resolved language
|
|
115
140
|
* (`config.language`, `--language`, the system's), following the human if they write in
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Word, PowerPoint and Excel originals as markdown: the SDK's `Read` refuses DOCX and PPTX
|
|
3
|
+
* (confirmed empirically), and course material is often both. The libraries are optional
|
|
4
|
+
* peer dependencies of the kit (`mammoth` for DOCX, `fflate` to open PPTX and XLSX, which
|
|
5
|
+
* are zips of XML); without them the caller is told what to install.
|
|
6
|
+
*/
|
|
7
|
+
export declare const EXTRACTABLE: Set<string>;
|
|
8
|
+
export declare class MissingLibraryError extends Error {
|
|
9
|
+
readonly library: string;
|
|
10
|
+
constructor(library: string, options?: ErrorOptions);
|
|
11
|
+
}
|
|
12
|
+
/** An original's text as markdown; `from`/`to` pick slides of a deck. */
|
|
13
|
+
export declare function extractText(file: string, from?: number, to?: number): Promise<string>;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Word, PowerPoint and Excel originals as markdown: the SDK's `Read` refuses DOCX and PPTX
|
|
5
|
+
* (confirmed empirically), and course material is often both. The libraries are optional
|
|
6
|
+
* peer dependencies of the kit (`mammoth` for DOCX, `fflate` to open PPTX and XLSX, which
|
|
7
|
+
* are zips of XML); without them the caller is told what to install.
|
|
8
|
+
*/
|
|
9
|
+
export const EXTRACTABLE = new Set(["docx", "pptx", "xlsx"]);
|
|
10
|
+
/** The most rows of a sheet written out; the rest are counted. */
|
|
11
|
+
const MAX_SHEET_ROWS = 200;
|
|
12
|
+
export class MissingLibraryError extends Error {
|
|
13
|
+
library;
|
|
14
|
+
constructor(library, options) {
|
|
15
|
+
super(`Reading this format needs the optional "${library}" library: npm install ${library}`, options);
|
|
16
|
+
this.library = library;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function load(library) {
|
|
20
|
+
try {
|
|
21
|
+
return (await import(library));
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw new MissingLibraryError(library, { cause: error });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const decode = (xml) => xml
|
|
28
|
+
.replace(/</g, "<")
|
|
29
|
+
.replace(/>/g, ">")
|
|
30
|
+
.replace(/"/g, '"')
|
|
31
|
+
.replace(/'/g, "'")
|
|
32
|
+
.replace(/&#(\d+);/g, (_, code) => String.fromCodePoint(Number(code)))
|
|
33
|
+
.replace(/&/g, "&");
|
|
34
|
+
/** The paragraphs of a DrawingML text body (slides and notes): each `<a:p>` its runs' `<a:t>` text. */
|
|
35
|
+
function paragraphs(xml) {
|
|
36
|
+
return [...xml.matchAll(/<a:p\b[\s\S]*?<\/a:p>/g)]
|
|
37
|
+
.map((p) => [...p[0].matchAll(/<a:t>([\s\S]*?)<\/a:t>/g)].map((t) => decode(t[1])).join(""))
|
|
38
|
+
.map((text) => text.trim())
|
|
39
|
+
.filter(Boolean);
|
|
40
|
+
}
|
|
41
|
+
const text = (files, name) => (files[name] ? new TextDecoder().decode(files[name]) : undefined);
|
|
42
|
+
async function unzip(file) {
|
|
43
|
+
const { unzipSync } = await load("fflate");
|
|
44
|
+
return unzipSync(new Uint8Array(await readFile(file)));
|
|
45
|
+
}
|
|
46
|
+
/** A slide deck: a section per slide (its title, text and speaker notes), from slide `from` to `to` (1-based). */
|
|
47
|
+
async function pptx(file, from, to) {
|
|
48
|
+
const files = await unzip(file);
|
|
49
|
+
const slides = Object.keys(files)
|
|
50
|
+
.map((name) => /^ppt\/slides\/slide(\d+)\.xml$/.exec(name))
|
|
51
|
+
.filter((m) => Boolean(m))
|
|
52
|
+
.map((m) => Number(m[1]))
|
|
53
|
+
.sort((a, b) => a - b);
|
|
54
|
+
const first = Math.max(1, from ?? 1);
|
|
55
|
+
const last = Math.min(slides.length, to ?? slides.length);
|
|
56
|
+
const parts = [`# ${path.basename(file)}`, `${slides.length} slides${first > 1 || last < slides.length ? `; slides ${first} to ${last}` : ""}.`];
|
|
57
|
+
let images = 0;
|
|
58
|
+
for (const n of slides.slice(first - 1, last)) {
|
|
59
|
+
const xml = text(files, `ppt/slides/slide${n}.xml`) ?? "";
|
|
60
|
+
images += (xml.match(/<p:pic\b/g) ?? []).length;
|
|
61
|
+
const [title, ...body] = paragraphs(xml);
|
|
62
|
+
parts.push(`## Slide ${n}${title ? `: ${title}` : ""}`);
|
|
63
|
+
if (body.length)
|
|
64
|
+
parts.push(body.map((line) => `- ${line}`).join("\n"));
|
|
65
|
+
// A slide's notes are linked from its relationships; their slide-number placeholder is left out.
|
|
66
|
+
const rels = text(files, `ppt/slides/_rels/slide${n}.xml.rels`) ?? "";
|
|
67
|
+
const notesTarget = /Target="\.\.\/notesSlides\/(notesSlide\d+\.xml)"/.exec(rels)?.[1];
|
|
68
|
+
const notes = notesTarget ? paragraphs(text(files, `ppt/notesSlides/${notesTarget}`) ?? "").filter((line) => !/^\d+$/.test(line)) : [];
|
|
69
|
+
if (notes.length)
|
|
70
|
+
parts.push(`Speaker notes:\n${notes.map((line) => `> ${line}`).join("\n")}`);
|
|
71
|
+
}
|
|
72
|
+
if (images)
|
|
73
|
+
parts.push(`(${images} images left out.)`);
|
|
74
|
+
return `${parts.join("\n\n")}\n`;
|
|
75
|
+
}
|
|
76
|
+
/** The column index of a cell reference ("C7" → 2). */
|
|
77
|
+
function columnOf(ref) {
|
|
78
|
+
let index = 0;
|
|
79
|
+
for (const letter of /^[A-Z]+/.exec(ref)?.[0] ?? "A")
|
|
80
|
+
index = index * 26 + letter.charCodeAt(0) - 64;
|
|
81
|
+
return index - 1;
|
|
82
|
+
}
|
|
83
|
+
const cell = (value) => value.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
84
|
+
/** A workbook: a markdown table per sheet, capped in rows. */
|
|
85
|
+
async function xlsx(file) {
|
|
86
|
+
const files = await unzip(file);
|
|
87
|
+
const shared = [...(text(files, "xl/sharedStrings.xml") ?? "").matchAll(/<si>([\s\S]*?)<\/si>/g)].map((si) => [...si[1].matchAll(/<t[^>]*>([\s\S]*?)<\/t>/g)].map((t) => decode(t[1])).join(""));
|
|
88
|
+
const workbook = text(files, "xl/workbook.xml") ?? "";
|
|
89
|
+
const names = [...workbook.matchAll(/<sheet\b[^>]*name="([^"]*)"/g)].map((m) => decode(m[1]));
|
|
90
|
+
const sheets = Object.keys(files)
|
|
91
|
+
.map((name) => /^xl\/worksheets\/sheet(\d+)\.xml$/.exec(name))
|
|
92
|
+
.filter((m) => Boolean(m))
|
|
93
|
+
.map((m) => Number(m[1]))
|
|
94
|
+
.sort((a, b) => a - b);
|
|
95
|
+
const parts = [`# ${path.basename(file)}`];
|
|
96
|
+
for (const n of sheets) {
|
|
97
|
+
const xml = text(files, `xl/worksheets/sheet${n}.xml`) ?? "";
|
|
98
|
+
const rows = [...xml.matchAll(/<row\b[\s\S]*?<\/row>/g)].map((row) => {
|
|
99
|
+
const values = [];
|
|
100
|
+
for (const c of row[0].matchAll(/<c\b([^>]*?)(?:\/>|>([\s\S]*?)<\/c>)/g)) {
|
|
101
|
+
const attrs = c[1];
|
|
102
|
+
const ref = /r="([A-Z]+\d+)"/.exec(attrs)?.[1] ?? "";
|
|
103
|
+
const type = /t="(\w+)"/.exec(attrs)?.[1];
|
|
104
|
+
const raw = /<v>([\s\S]*?)<\/v>/.exec(c[2] ?? "")?.[1] ?? /<t[^>]*>([\s\S]*?)<\/t>/.exec(c[2] ?? "")?.[1] ?? "";
|
|
105
|
+
const value = type === "s" ? (shared[Number(raw)] ?? "") : decode(raw);
|
|
106
|
+
values[ref ? columnOf(ref) : values.length] = value;
|
|
107
|
+
}
|
|
108
|
+
return Array.from(values, (v) => cell(v ?? ""));
|
|
109
|
+
});
|
|
110
|
+
const filled = rows.filter((row) => row.some(Boolean));
|
|
111
|
+
parts.push(`## ${names[n - 1] ?? `Sheet ${n}`}`);
|
|
112
|
+
if (!filled.length) {
|
|
113
|
+
parts.push("(empty)");
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const width = Math.max(...filled.map((row) => row.length));
|
|
117
|
+
const pad = (row) => `| ${Array.from({ length: width }, (_, i) => row[i] ?? "").join(" | ")} |`;
|
|
118
|
+
const shown = filled.slice(0, MAX_SHEET_ROWS);
|
|
119
|
+
parts.push([pad(shown[0]), `|${" --- |".repeat(width)}`, ...shown.slice(1).map(pad)].join("\n"));
|
|
120
|
+
if (filled.length > MAX_SHEET_ROWS)
|
|
121
|
+
parts.push(`(${filled.length - MAX_SHEET_ROWS} more rows.)`);
|
|
122
|
+
}
|
|
123
|
+
return `${parts.join("\n\n")}\n`;
|
|
124
|
+
}
|
|
125
|
+
/** A Word document: headings, paragraphs, lists and tables. */
|
|
126
|
+
async function docx(file) {
|
|
127
|
+
const mammoth = await load("mammoth");
|
|
128
|
+
const { value: html } = await mammoth.convertToHtml({ path: file });
|
|
129
|
+
const images = (html.match(/<img\b/g) ?? []).length;
|
|
130
|
+
// Markdown with turndown (also optional); plain text without it.
|
|
131
|
+
const turndown = await load("turndown").catch(() => null);
|
|
132
|
+
const body = turndown
|
|
133
|
+
? new turndown.default({ headingStyle: "atx" }).turndown(html.replace(/<img\b[^>]*>/g, ""))
|
|
134
|
+
: decode(html.replace(/<\/(p|h\d|li|tr)>/g, "\n").replace(/<[^>]+>/g, "")).trim();
|
|
135
|
+
return `# ${path.basename(file)}\n\n${body}\n${images ? `\n(${images} images left out.)\n` : ""}`;
|
|
136
|
+
}
|
|
137
|
+
/** An original's text as markdown; `from`/`to` pick slides of a deck. */
|
|
138
|
+
export async function extractText(file, from, to) {
|
|
139
|
+
const type = path.extname(file).slice(1).toLowerCase();
|
|
140
|
+
if (type === "docx")
|
|
141
|
+
return docx(file);
|
|
142
|
+
if (type === "pptx")
|
|
143
|
+
return pptx(file, from, to);
|
|
144
|
+
if (type === "xlsx")
|
|
145
|
+
return xlsx(file);
|
|
146
|
+
throw new Error(`extract_text reads DOCX, PPTX and XLSX; for "${path.basename(file)}" use Read (PDFs, images, text).`);
|
|
147
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type KnowledgeStore, type PageType } from "./knowledgeStore.js";
|
|
2
|
+
/** Options of `createFileKnowledgeStore()`. */
|
|
3
|
+
export interface FileKnowledgeStoreOptions {
|
|
4
|
+
/** The agent's own page types, besides the kit's four. */
|
|
5
|
+
pageTypes?: readonly PageType[];
|
|
6
|
+
/** The sources folder: a summary's `file` field is given relative to it. */
|
|
7
|
+
sourcesDir?: string;
|
|
8
|
+
}
|
|
9
|
+
/** A `KnowledgeStore` over the markdown files of `knowledgeDir`. */
|
|
10
|
+
export declare function createFileKnowledgeStore(knowledgeDir: string, options?: FileKnowledgeStoreOptions): KnowledgeStore;
|