@astralyn/pi 0.82.4 → 0.82.6
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/CHANGELOG.md +52 -0
- package/dist/cli/project-trust.d.ts.map +1 -1
- package/dist/cli/project-trust.js +4 -1
- package/dist/cli/project-trust.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +2 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +4 -22
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +2 -3
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +37 -5
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +11 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +3 -13
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +6 -36
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/extensions/plan/constants.d.ts +11 -2
- package/dist/extensions/plan/constants.d.ts.map +1 -1
- package/dist/extensions/plan/constants.js +14 -2
- package/dist/extensions/plan/constants.js.map +1 -1
- package/dist/extensions/plan/index.d.ts +0 -18
- package/dist/extensions/plan/index.d.ts.map +1 -1
- package/dist/extensions/plan/index.js +35 -3
- package/dist/extensions/plan/index.js.map +1 -1
- package/dist/extensions/plan/view.d.ts +36 -0
- package/dist/extensions/plan/view.d.ts.map +1 -0
- package/dist/extensions/plan/view.js +74 -0
- package/dist/extensions/plan/view.js.map +1 -0
- package/dist/extensions/question/dialog.d.ts +3 -1
- package/dist/extensions/question/dialog.d.ts.map +1 -1
- package/dist/extensions/question/dialog.js +106 -52
- package/dist/extensions/question/dialog.js.map +1 -1
- package/dist/extensions/question/index.d.ts.map +1 -1
- package/dist/extensions/question/index.js +2 -2
- package/dist/extensions/question/index.js.map +1 -1
- package/dist/extensions/question/render-cache.d.ts +9 -0
- package/dist/extensions/question/render-cache.d.ts.map +1 -1
- package/dist/extensions/question/render-cache.js +22 -0
- package/dist/extensions/question/render-cache.js.map +1 -1
- package/dist/extensions/question/results.d.ts.map +1 -1
- package/dist/extensions/question/results.js +6 -1
- package/dist/extensions/question/results.js.map +1 -1
- package/dist/extensions/question/schema.d.ts +4 -0
- package/dist/extensions/question/schema.d.ts.map +1 -1
- package/dist/extensions/question/schema.js +19 -28
- package/dist/extensions/question/schema.js.map +1 -1
- package/dist/extensions/question/types.d.ts +1 -1
- package/dist/extensions/question/types.d.ts.map +1 -1
- package/dist/extensions/question/types.js.map +1 -1
- package/dist/extensions/router/stream.d.ts.map +1 -1
- package/dist/extensions/router/stream.js +44 -0
- package/dist/extensions/router/stream.js.map +1 -1
- package/dist/extensions/subagent/activity.d.ts.map +1 -1
- package/dist/extensions/subagent/activity.js +25 -4
- package/dist/extensions/subagent/activity.js.map +1 -1
- package/dist/extensions/subagent/agents.d.ts.map +1 -1
- package/dist/extensions/subagent/agents.js +12 -6
- package/dist/extensions/subagent/agents.js.map +1 -1
- package/dist/extensions/subagent/constants.d.ts +1 -1
- package/dist/extensions/subagent/constants.d.ts.map +1 -1
- package/dist/extensions/subagent/constants.js +3 -2
- package/dist/extensions/subagent/constants.js.map +1 -1
- package/dist/extensions/subagent/index.d.ts.map +1 -1
- package/dist/extensions/subagent/index.js +14 -0
- package/dist/extensions/subagent/index.js.map +1 -1
- package/dist/extensions/subagent/render.d.ts.map +1 -1
- package/dist/extensions/subagent/render.js +243 -116
- package/dist/extensions/subagent/render.js.map +1 -1
- package/dist/extensions/subagent/resolve.d.ts.map +1 -1
- package/dist/extensions/subagent/resolve.js +13 -9
- package/dist/extensions/subagent/resolve.js.map +1 -1
- package/dist/extensions/subagent/runner.d.ts +1 -1
- package/dist/extensions/subagent/runner.d.ts.map +1 -1
- package/dist/extensions/subagent/runner.js +9 -3
- package/dist/extensions/subagent/runner.js.map +1 -1
- package/dist/extensions/subagent/schema.d.ts.map +1 -1
- package/dist/extensions/subagent/schema.js +4 -4
- package/dist/extensions/subagent/schema.js.map +1 -1
- package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
- package/dist/extensions/subagent/sdk-runner.js +5 -0
- package/dist/extensions/subagent/sdk-runner.js.map +1 -1
- package/dist/extensions/subagent/types.d.ts +7 -0
- package/dist/extensions/subagent/types.d.ts.map +1 -1
- package/dist/extensions/subagent/types.js.map +1 -1
- package/dist/extensions/todo/constants.d.ts +17 -6
- package/dist/extensions/todo/constants.d.ts.map +1 -1
- package/dist/extensions/todo/constants.js +47 -9
- package/dist/extensions/todo/constants.js.map +1 -1
- package/dist/extensions/todo/index.d.ts +1 -15
- package/dist/extensions/todo/index.d.ts.map +1 -1
- package/dist/extensions/todo/index.js +35 -4
- package/dist/extensions/todo/index.js.map +1 -1
- package/dist/extensions/todo/schema.d.ts +59 -7
- package/dist/extensions/todo/schema.d.ts.map +1 -1
- package/dist/extensions/todo/schema.js +72 -12
- package/dist/extensions/todo/schema.js.map +1 -1
- package/dist/extensions/todo/state.d.ts +20 -5
- package/dist/extensions/todo/state.d.ts.map +1 -1
- package/dist/extensions/todo/state.js +1016 -134
- package/dist/extensions/todo/state.js.map +1 -1
- package/dist/extensions/todo/view.d.ts +16 -1
- package/dist/extensions/todo/view.d.ts.map +1 -1
- package/dist/extensions/todo/view.js +293 -7
- package/dist/extensions/todo/view.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +6 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +14 -6
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +6 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +41 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +8 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/coalescing-terminal.d.ts +71 -17
- package/dist/utils/coalescing-terminal.d.ts.map +1 -1
- package/dist/utils/coalescing-terminal.js +136 -31
- package/dist/utils/coalescing-terminal.js.map +1 -1
- package/docs/bundled/extensions/plan.md +6 -4
- package/docs/bundled/extensions/question.md +9 -7
- package/docs/bundled/extensions/subagent.md +4 -4
- package/docs/bundled/extensions/todo.md +9 -5
- package/docs/bundled/tool-presentation.md +1 -1
- package/docs/extensions.md +14 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/utils/output-decoder.d.ts +0 -52
- package/dist/utils/output-decoder.d.ts.map +0 -1
- package/dist/utils/output-decoder.js +0 -134
- package/dist/utils/output-decoder.js.map +0 -1
|
@@ -1,20 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* todo/constants.ts — tool identity, command name, and prompt copy.
|
|
3
|
-
*
|
|
4
|
-
* Name/label/command live here so `index.ts` only assembles the tool, matching
|
|
5
|
-
* the question/deepwiki constants.ts pattern. Prompt copy is the model-facing
|
|
6
|
-
* contract; keep it stable.
|
|
2
|
+
* todo/constants.ts — tool identity, command name, input bounds, and prompt copy.
|
|
7
3
|
*/
|
|
8
4
|
export const TODO_TOOL_NAME = "todo";
|
|
9
5
|
export const TODO_TOOL_LABEL = "Todo";
|
|
10
6
|
export const TODOS_COMMAND_NAME = "todos";
|
|
11
7
|
/** Max tasks shown in a single `list` tool result body (model-facing). */
|
|
12
8
|
export const LIST_DISPLAY_MAX_ITEMS = 50;
|
|
13
|
-
export const
|
|
9
|
+
export const TODO_MAX_LIST_LIMIT = LIST_DISPLAY_MAX_ITEMS;
|
|
10
|
+
export const TODO_MAX_LIST_QUERY_LENGTH = 200;
|
|
11
|
+
/** Input limits keep task snapshots and model-facing output bounded. */
|
|
12
|
+
export const TODO_MAX_BATCH_ITEMS = 20;
|
|
13
|
+
export const TODO_MAX_SUBJECT_LENGTH = 240;
|
|
14
|
+
export const TODO_MAX_DESCRIPTION_LENGTH = 4_000;
|
|
15
|
+
export const TODO_MAX_ACTIVE_FORM_LENGTH = 240;
|
|
16
|
+
export const TODO_MAX_OWNER_LENGTH = 160;
|
|
17
|
+
export const TODO_MAX_BATCH_KEY_LENGTH = 120;
|
|
18
|
+
export const TODO_MAX_BLOCKED_BY = 50;
|
|
19
|
+
/** Metadata is JSON-like, deeply copied, and bounded independently of task text. */
|
|
20
|
+
export const TODO_MAX_METADATA_KEY_LENGTH = 160;
|
|
21
|
+
export const TODO_MAX_METADATA_DEPTH = 8;
|
|
22
|
+
export const TODO_MAX_METADATA_ENTRIES = 100;
|
|
23
|
+
export const TODO_MAX_METADATA_BYTES = 16_384;
|
|
24
|
+
export const TODO_TOOL_DESCRIPTION = `Manage the conversation's task list for multi-step coding work.
|
|
25
|
+
|
|
26
|
+
## When to Use
|
|
27
|
+
- Work with three or more meaningful steps, user-provided task lists, or long sessions where progress can drift
|
|
28
|
+
- Capture new user requirements as tasks right away; after completing a task, add follow-up tasks discovered during implementation
|
|
29
|
+
|
|
30
|
+
## When NOT to Use
|
|
31
|
+
- Trivial single-step tasks and simple Q&A — just do the work
|
|
32
|
+
|
|
33
|
+
## Actions
|
|
34
|
+
- create: add one pending task; subject (short, imperative, reviewable scope) and description (what done means: acceptance criteria or verification detail) are required
|
|
35
|
+
- create_many: atomically add up to 20 pending tasks in input order; each item needs subject and description, and optional unique keys can be linked within the batch with blockedByKeys
|
|
36
|
+
- update: change status or details, and manage dependencies via addBlockedBy/removeBlockedBy (what this task waits on) and addBlocks/removeBlocks (what waits on this task); dependencies are validated and cycle-checked
|
|
37
|
+
- list: one-line summaries with unresolved blockers, optionally filtered by status, paged with limit/afterId, searched with query, or narrowed with unblockedOnly; get: one task in full; delete: tombstone an obsolete task and release its dependents; clear: requires confirm=true and the exact expectedCount, without reusing ids
|
|
38
|
+
- Parameters that do not apply to the chosen action are rejected with guidance
|
|
39
|
+
|
|
40
|
+
## Status Workflow
|
|
41
|
+
pending -> in_progress -> completed; deleted is the only terminal state. Keep exactly one task in_progress (starting one demotes any other active task): mark it in_progress BEFORE starting the work, and completed only when implementation and verification are genuinely done — never with failing tests or partial work; reopen instead of duplicating. When blocked, do not fake completion: create a task for the blocker and link it with addBlockedBy.
|
|
42
|
+
|
|
43
|
+
## Examples
|
|
44
|
+
{"action": "create", "subject": "Fix login redirect", "description": "Redirect lands on the dashboard; auth tests pass"}
|
|
45
|
+
{"action": "create_many", "items": [{"key": "parser", "subject": "Wire parser", "description": "Parser handles config"}, {"subject": "Test parser", "description": "Parser tests pass", "blockedByKeys": ["parser"]}]}
|
|
46
|
+
{"action": "update", "id": 2, "status": "in_progress", "activeForm": "fixing login redirect"}
|
|
47
|
+
{"action": "update", "id": 1, "addBlocks": [2, 3]}
|
|
48
|
+
|
|
49
|
+
## Tips
|
|
50
|
+
- list before create to avoid duplicates; prefer working in id order — earlier tasks usually set up context for later ones
|
|
51
|
+
- Use dependencies only for real ordering constraints, and delete obsolete tasks promptly so their dependents are released`;
|
|
14
52
|
export const TODO_PROMPT_SNIPPET = "Track multi-step coding work with a small outcome-oriented task list";
|
|
15
53
|
export const TODO_PROMPT_GUIDELINES = [
|
|
16
|
-
"Use `todo` for work with three or more meaningful steps, user-provided task lists, or long sessions where progress can drift;
|
|
17
|
-
"
|
|
18
|
-
"Keep the `todo` list short and outcome-oriented; use
|
|
54
|
+
"Use `todo` for work with three or more meaningful steps, user-provided task lists, or long sessions where progress can drift; list before creating to avoid duplicates, use create_many for a known batch of up to 20 tasks, and capture follow-up work discovered during implementation as new tasks.",
|
|
55
|
+
"Mark the active `todo` task in_progress before starting work (exactly one at a time; the tool demotes any other active item) and completed immediately after verification — never while tests fail or work is partial; reopen instead of duplicating, and when blocked create a task for the blocker and link it with addBlockedBy.",
|
|
56
|
+
"Keep the `todo` list short and outcome-oriented; prefer working in id order, use dependencies only for real prerequisites, and delete obsolete items promptly so their dependents are released. Clear only with confirm=true and the current expectedCount.",
|
|
19
57
|
];
|
|
20
58
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/extensions/todo/constants.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/extensions/todo/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AACrC,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC;AACtC,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAE1C,0EAA0E;AAC1E,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AACzC,MAAM,CAAC,MAAM,mBAAmB,GAAG,sBAAsB,CAAC;AAC1D,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,CAAC;AAE9C,wEAAwE;AACxE,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AACvC,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AAC3C,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAC;AACjD,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAC/C,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AACzC,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAC7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,oFAAoF;AACpF,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAG,CAAC;AAChD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AACzC,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAC7C,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAE9C,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;2HA2BsF,CAAC;AAE5H,MAAM,CAAC,MAAM,mBAAmB,GAAG,sEAAsE,CAAC;AAE1G,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACrC,wSAAwS;IACxS,uUAAqU;IACrU,6PAA6P;CAC7P,CAAC","sourcesContent":["/**\n * todo/constants.ts — tool identity, command name, input bounds, and prompt copy.\n */\n\nexport const TODO_TOOL_NAME = \"todo\";\nexport const TODO_TOOL_LABEL = \"Todo\";\nexport const TODOS_COMMAND_NAME = \"todos\";\n\n/** Max tasks shown in a single `list` tool result body (model-facing). */\nexport const LIST_DISPLAY_MAX_ITEMS = 50;\nexport const TODO_MAX_LIST_LIMIT = LIST_DISPLAY_MAX_ITEMS;\nexport const TODO_MAX_LIST_QUERY_LENGTH = 200;\n\n/** Input limits keep task snapshots and model-facing output bounded. */\nexport const TODO_MAX_BATCH_ITEMS = 20;\nexport const TODO_MAX_SUBJECT_LENGTH = 240;\nexport const TODO_MAX_DESCRIPTION_LENGTH = 4_000;\nexport const TODO_MAX_ACTIVE_FORM_LENGTH = 240;\nexport const TODO_MAX_OWNER_LENGTH = 160;\nexport const TODO_MAX_BATCH_KEY_LENGTH = 120;\nexport const TODO_MAX_BLOCKED_BY = 50;\n\n/** Metadata is JSON-like, deeply copied, and bounded independently of task text. */\nexport const TODO_MAX_METADATA_KEY_LENGTH = 160;\nexport const TODO_MAX_METADATA_DEPTH = 8;\nexport const TODO_MAX_METADATA_ENTRIES = 100;\nexport const TODO_MAX_METADATA_BYTES = 16_384;\n\nexport const TODO_TOOL_DESCRIPTION = `Manage the conversation's task list for multi-step coding work.\n\n## When to Use\n- Work with three or more meaningful steps, user-provided task lists, or long sessions where progress can drift\n- Capture new user requirements as tasks right away; after completing a task, add follow-up tasks discovered during implementation\n\n## When NOT to Use\n- Trivial single-step tasks and simple Q&A — just do the work\n\n## Actions\n- create: add one pending task; subject (short, imperative, reviewable scope) and description (what done means: acceptance criteria or verification detail) are required\n- create_many: atomically add up to 20 pending tasks in input order; each item needs subject and description, and optional unique keys can be linked within the batch with blockedByKeys\n- update: change status or details, and manage dependencies via addBlockedBy/removeBlockedBy (what this task waits on) and addBlocks/removeBlocks (what waits on this task); dependencies are validated and cycle-checked\n- list: one-line summaries with unresolved blockers, optionally filtered by status, paged with limit/afterId, searched with query, or narrowed with unblockedOnly; get: one task in full; delete: tombstone an obsolete task and release its dependents; clear: requires confirm=true and the exact expectedCount, without reusing ids\n- Parameters that do not apply to the chosen action are rejected with guidance\n\n## Status Workflow\npending -> in_progress -> completed; deleted is the only terminal state. Keep exactly one task in_progress (starting one demotes any other active task): mark it in_progress BEFORE starting the work, and completed only when implementation and verification are genuinely done — never with failing tests or partial work; reopen instead of duplicating. When blocked, do not fake completion: create a task for the blocker and link it with addBlockedBy.\n\n## Examples\n{\"action\": \"create\", \"subject\": \"Fix login redirect\", \"description\": \"Redirect lands on the dashboard; auth tests pass\"}\n{\"action\": \"create_many\", \"items\": [{\"key\": \"parser\", \"subject\": \"Wire parser\", \"description\": \"Parser handles config\"}, {\"subject\": \"Test parser\", \"description\": \"Parser tests pass\", \"blockedByKeys\": [\"parser\"]}]}\n{\"action\": \"update\", \"id\": 2, \"status\": \"in_progress\", \"activeForm\": \"fixing login redirect\"}\n{\"action\": \"update\", \"id\": 1, \"addBlocks\": [2, 3]}\n\n## Tips\n- list before create to avoid duplicates; prefer working in id order — earlier tasks usually set up context for later ones\n- Use dependencies only for real ordering constraints, and delete obsolete tasks promptly so their dependents are released`;\n\nexport const TODO_PROMPT_SNIPPET = \"Track multi-step coding work with a small outcome-oriented task list\";\n\nexport const TODO_PROMPT_GUIDELINES = [\n\t\"Use `todo` for work with three or more meaningful steps, user-provided task lists, or long sessions where progress can drift; list before creating to avoid duplicates, use create_many for a known batch of up to 20 tasks, and capture follow-up work discovered during implementation as new tasks.\",\n\t\"Mark the active `todo` task in_progress before starting work (exactly one at a time; the tool demotes any other active item) and completed immediately after verification — never while tests fail or work is partial; reopen instead of duplicating, and when blocked create a task for the blocker and link it with addBlockedBy.\",\n\t\"Keep the `todo` list short and outcome-oriented; prefer working in id order, use dependencies only for real prerequisites, and delete obsolete items promptly so their dependents are released. Clear only with confirm=true and the current expectedCount.\",\n];\n"]}
|
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
* todo — Pi-native task tracking for multi-step work.
|
|
3
|
-
*
|
|
4
|
-
* The state is intentionally conversation-backed: every tool result carries a
|
|
5
|
-
* full snapshot in `details`, and lifecycle handlers replay the current branch.
|
|
6
|
-
* This keeps /reload, compaction, and session-tree navigation aligned with the
|
|
7
|
-
* conversation without adding a separate disk database. (Compaction-safe by
|
|
8
|
-
* design: sessionManager.getBranch() returns the FULL branch history including
|
|
9
|
-
* pre-compaction toolResult entries — only buildSessionContext summarizes.)
|
|
10
|
-
*
|
|
11
|
-
* State is keyed per session id (see state.ts): resume and /tree switches can
|
|
12
|
-
* change the session within one process, and execute + lifecycle handlers
|
|
13
|
-
* re-point the active bucket before touching state.
|
|
14
|
-
*/
|
|
15
|
-
import type { ExtensionAPI } from "../../core/extensions/types.ts";
|
|
1
|
+
import { type ExtensionAPI } from "../../core/extensions/types.ts";
|
|
16
2
|
export default function todo(pi: ExtensionAPI): void;
|
|
17
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/todo/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/todo/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAEN,KAAK,YAAY,EAGjB,MAAM,gCAAgC,CAAC;AAyCxC,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAuFnD","sourcesContent":["/**\n * todo — Pi-native task tracking for multi-step work.\n *\n * The state is intentionally conversation-backed: every tool result carries a\n * full snapshot in `details`, and lifecycle handlers replay the current branch.\n * This keeps /reload, compaction, and session-tree navigation aligned with the\n * conversation without adding a separate disk database. (Compaction-safe by\n * design: sessionManager.getBranch() returns the FULL branch history including\n * pre-compaction toolResult entries — only buildSessionContext summarizes.)\n *\n * State is keyed per session id (see state.ts): resume and /tree switches can\n * change the session within one process, and execute + lifecycle handlers\n * re-point the active bucket before touching state.\n */\nimport { Text } from \"@earendil-works/pi-tui\";\nimport {\n\ttype AgentToolResult,\n\ttype ExtensionAPI,\n\tisStaleExtensionContextError,\n\ttype ToolExecutionMode,\n} from \"../../core/extensions/types.ts\";\nimport {\n\tTODO_PROMPT_GUIDELINES,\n\tTODO_PROMPT_SNIPPET,\n\tTODO_TOOL_DESCRIPTION,\n\tTODO_TOOL_LABEL,\n\tTODO_TOOL_NAME,\n\tTODOS_COMMAND_NAME,\n} from \"./constants.ts\";\nimport { TodoOverlay } from \"./overlay.ts\";\nimport { type TodoDetails, TodoParamsSchema } from \"./schema.ts\";\nimport {\n\tapplyTodoMutation,\n\tbuildTodoDetails,\n\tcommitTodoState,\n\tdisposeTodoSession,\n\tformatTodoContent,\n\tgetTodoState,\n\treplaceTodoState,\n\treplayTodosFromBranch,\n\tsetActiveTodoSession,\n} from \"./state.ts\";\nimport { formatCommandList, formatTodoCall, formatTodoGroupCall } from \"./view.ts\";\n\ninterface TodoSessionCtx {\n\tsessionManager: { getBranch(): Iterable<unknown>; getSessionId(): string };\n}\n\nfunction safeReplay(ctx: TodoSessionCtx): void {\n\t// Every ctx.sessionManager access goes through a stale guard, so the whole\n\t// body stays inside the try: a lifecycle event can race session replacement\n\t// (resume, /tree, /reload), and a stale ctx just means another session took\n\t// over — nothing left to replay for it.\n\ttry {\n\t\tsetActiveTodoSession(ctx.sessionManager.getSessionId());\n\t\treplaceTodoState(replayTodosFromBranch(ctx));\n\t} catch (error) {\n\t\tif (!isStaleExtensionContextError(error)) throw error;\n\t}\n}\n\nexport default function todo(pi: ExtensionAPI): void {\n\tlet overlay: TodoOverlay | undefined;\n\n\tpi.registerTool({\n\t\tname: TODO_TOOL_NAME,\n\t\tlabel: TODO_TOOL_LABEL,\n\t\tdescription: TODO_TOOL_DESCRIPTION,\n\t\tpromptSnippet: TODO_PROMPT_SNIPPET,\n\t\tpromptGuidelines: TODO_PROMPT_GUIDELINES,\n\t\tparameters: TodoParamsSchema,\n\t\texecutionMode: \"sequential\" as ToolExecutionMode,\n\t\t// Consecutive todo calls collapse into one run like read/find's `explore`\n\t\t// group; the overlay already carries the live list, so the transcript only\n\t\t// needs the sequence of operations.\n\t\ttoolGroup: TODO_TOOL_NAME,\n\n\t\tasync execute(_toolCallId, params, _signal, _onUpdate, ctx): Promise<AgentToolResult<TodoDetails>> {\n\t\t\t// Re-point the active bucket: resume//tree can switch sessions between\n\t\t\t// lifecycle events and this call.\n\t\t\tsetActiveTodoSession(ctx.sessionManager.getSessionId());\n\t\t\tconst result = applyTodoMutation(getTodoState(), params);\n\t\t\tif (result.operation.kind === \"error\") {\n\t\t\t\tthrow new Error(result.operation.message);\n\t\t\t}\n\t\t\tcommitTodoState(result.state);\n\t\t\tconst text = formatTodoContent(result.operation, result.state);\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\tdetails: buildTodoDetails(params, result.state),\n\t\t\t};\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(\n\t\t\t\tcontext.toolGroupSummary\n\t\t\t\t\t? formatTodoGroupCall(args, theme, context)\n\t\t\t\t\t: formatTodoCall(args, theme, context.expanded),\n\t\t\t);\n\t\t\treturn text;\n\t\t},\n\t});\n\n\tpi.registerCommand(TODOS_COMMAND_NAME, {\n\t\tdescription: \"Show todos for the current conversation branch\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tif (!ctx.hasUI) {\n\t\t\t\tctx.ui.notify(\"/todos requires an interactive UI.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsetActiveTodoSession(ctx.sessionManager.getSessionId());\n\t\t\tctx.ui.notify(formatCommandList(getTodoState()), \"info\");\n\t\t},\n\t});\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tsafeReplay(ctx);\n\t\tif (ctx.hasUI) {\n\t\t\toverlay ??= new TodoOverlay();\n\t\t\toverlay.setUI(ctx.ui);\n\t\t\toverlay.resetVisibility();\n\t\t\toverlay.update();\n\t\t}\n\t});\n\n\tpi.on(\"session_compact\", async (_event, ctx) => {\n\t\tsafeReplay(ctx);\n\t\toverlay?.resetVisibility();\n\t\toverlay?.update();\n\t});\n\n\tpi.on(\"session_tree\", async (_event, ctx) => {\n\t\tsafeReplay(ctx);\n\t\toverlay?.resetVisibility();\n\t\toverlay?.update();\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tdisposeTodoSession(ctx.sessionManager.getSessionId());\n\t\toverlay?.dispose();\n\t\toverlay = undefined;\n\t});\n\n\tpi.on(\"tool_execution_end\", async (event) => {\n\t\tif (event.toolName !== TODO_TOOL_NAME || event.isError) return;\n\t\toverlay?.update();\n\t});\n}\n"]}
|
|
@@ -1,15 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* todo — Pi-native task tracking for multi-step work.
|
|
3
|
+
*
|
|
4
|
+
* The state is intentionally conversation-backed: every tool result carries a
|
|
5
|
+
* full snapshot in `details`, and lifecycle handlers replay the current branch.
|
|
6
|
+
* This keeps /reload, compaction, and session-tree navigation aligned with the
|
|
7
|
+
* conversation without adding a separate disk database. (Compaction-safe by
|
|
8
|
+
* design: sessionManager.getBranch() returns the FULL branch history including
|
|
9
|
+
* pre-compaction toolResult entries — only buildSessionContext summarizes.)
|
|
10
|
+
*
|
|
11
|
+
* State is keyed per session id (see state.ts): resume and /tree switches can
|
|
12
|
+
* change the session within one process, and execute + lifecycle handlers
|
|
13
|
+
* re-point the active bucket before touching state.
|
|
14
|
+
*/
|
|
15
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
16
|
+
import { isStaleExtensionContextError, } from "../../core/extensions/types.js";
|
|
1
17
|
import { TODO_PROMPT_GUIDELINES, TODO_PROMPT_SNIPPET, TODO_TOOL_DESCRIPTION, TODO_TOOL_LABEL, TODO_TOOL_NAME, TODOS_COMMAND_NAME, } from "./constants.js";
|
|
2
18
|
import { TodoOverlay } from "./overlay.js";
|
|
3
19
|
import { TodoParamsSchema } from "./schema.js";
|
|
4
20
|
import { applyTodoMutation, buildTodoDetails, commitTodoState, disposeTodoSession, formatTodoContent, getTodoState, replaceTodoState, replayTodosFromBranch, setActiveTodoSession, } from "./state.js";
|
|
5
|
-
import { formatCommandList } from "./view.js";
|
|
21
|
+
import { formatCommandList, formatTodoCall, formatTodoGroupCall } from "./view.js";
|
|
6
22
|
function safeReplay(ctx) {
|
|
7
|
-
|
|
23
|
+
// Every ctx.sessionManager access goes through a stale guard, so the whole
|
|
24
|
+
// body stays inside the try: a lifecycle event can race session replacement
|
|
25
|
+
// (resume, /tree, /reload), and a stale ctx just means another session took
|
|
26
|
+
// over — nothing left to replay for it.
|
|
8
27
|
try {
|
|
28
|
+
setActiveTodoSession(ctx.sessionManager.getSessionId());
|
|
9
29
|
replaceTodoState(replayTodosFromBranch(ctx));
|
|
10
30
|
}
|
|
11
31
|
catch (error) {
|
|
12
|
-
if (
|
|
32
|
+
if (!isStaleExtensionContextError(error))
|
|
13
33
|
throw error;
|
|
14
34
|
}
|
|
15
35
|
}
|
|
@@ -23,6 +43,10 @@ export default function todo(pi) {
|
|
|
23
43
|
promptGuidelines: TODO_PROMPT_GUIDELINES,
|
|
24
44
|
parameters: TodoParamsSchema,
|
|
25
45
|
executionMode: "sequential",
|
|
46
|
+
// Consecutive todo calls collapse into one run like read/find's `explore`
|
|
47
|
+
// group; the overlay already carries the live list, so the transcript only
|
|
48
|
+
// needs the sequence of operations.
|
|
49
|
+
toolGroup: TODO_TOOL_NAME,
|
|
26
50
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
27
51
|
// Re-point the active bucket: resume//tree can switch sessions between
|
|
28
52
|
// lifecycle events and this call.
|
|
@@ -35,9 +59,16 @@ export default function todo(pi) {
|
|
|
35
59
|
const text = formatTodoContent(result.operation, result.state);
|
|
36
60
|
return {
|
|
37
61
|
content: [{ type: "text", text }],
|
|
38
|
-
details: buildTodoDetails(params, result.state
|
|
62
|
+
details: buildTodoDetails(params, result.state),
|
|
39
63
|
};
|
|
40
64
|
},
|
|
65
|
+
renderCall(args, theme, context) {
|
|
66
|
+
const text = context.lastComponent ?? new Text("", 0, 0);
|
|
67
|
+
text.setText(context.toolGroupSummary
|
|
68
|
+
? formatTodoGroupCall(args, theme, context)
|
|
69
|
+
: formatTodoCall(args, theme, context.expanded));
|
|
70
|
+
return text;
|
|
71
|
+
},
|
|
41
72
|
});
|
|
42
73
|
pi.registerCommand(TODOS_COMMAND_NAME, {
|
|
43
74
|
description: "Show todos for the current conversation branch",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/todo/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/todo/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * todo — Pi-native task tracking for multi-step work.\n *\n * The state is intentionally conversation-backed: every tool result carries a\n * full snapshot in `details`, and lifecycle handlers replay the current branch.\n * This keeps /reload, compaction, and session-tree navigation aligned with the\n * conversation without adding a separate disk database. (Compaction-safe by\n * design: sessionManager.getBranch() returns the FULL branch history including\n * pre-compaction toolResult entries — only buildSessionContext summarizes.)\n *\n * State is keyed per session id (see state.ts): resume and /tree switches can\n * change the session within one process, and execute + lifecycle handlers\n * re-point the active bucket before touching state.\n */\nimport { Text } from \"@earendil-works/pi-tui\";\nimport {\n\ttype AgentToolResult,\n\ttype ExtensionAPI,\n\tisStaleExtensionContextError,\n\ttype ToolExecutionMode,\n} from \"../../core/extensions/types.ts\";\nimport {\n\tTODO_PROMPT_GUIDELINES,\n\tTODO_PROMPT_SNIPPET,\n\tTODO_TOOL_DESCRIPTION,\n\tTODO_TOOL_LABEL,\n\tTODO_TOOL_NAME,\n\tTODOS_COMMAND_NAME,\n} from \"./constants.ts\";\nimport { TodoOverlay } from \"./overlay.ts\";\nimport { type TodoDetails, TodoParamsSchema } from \"./schema.ts\";\nimport {\n\tapplyTodoMutation,\n\tbuildTodoDetails,\n\tcommitTodoState,\n\tdisposeTodoSession,\n\tformatTodoContent,\n\tgetTodoState,\n\treplaceTodoState,\n\treplayTodosFromBranch,\n\tsetActiveTodoSession,\n} from \"./state.ts\";\nimport { formatCommandList, formatTodoCall, formatTodoGroupCall } from \"./view.ts\";\n\ninterface TodoSessionCtx {\n\tsessionManager: { getBranch(): Iterable<unknown>; getSessionId(): string };\n}\n\nfunction safeReplay(ctx: TodoSessionCtx): void {\n\t// Every ctx.sessionManager access goes through a stale guard, so the whole\n\t// body stays inside the try: a lifecycle event can race session replacement\n\t// (resume, /tree, /reload), and a stale ctx just means another session took\n\t// over — nothing left to replay for it.\n\ttry {\n\t\tsetActiveTodoSession(ctx.sessionManager.getSessionId());\n\t\treplaceTodoState(replayTodosFromBranch(ctx));\n\t} catch (error) {\n\t\tif (!isStaleExtensionContextError(error)) throw error;\n\t}\n}\n\nexport default function todo(pi: ExtensionAPI): void {\n\tlet overlay: TodoOverlay | undefined;\n\n\tpi.registerTool({\n\t\tname: TODO_TOOL_NAME,\n\t\tlabel: TODO_TOOL_LABEL,\n\t\tdescription: TODO_TOOL_DESCRIPTION,\n\t\tpromptSnippet: TODO_PROMPT_SNIPPET,\n\t\tpromptGuidelines: TODO_PROMPT_GUIDELINES,\n\t\tparameters: TodoParamsSchema,\n\t\texecutionMode: \"sequential\" as ToolExecutionMode,\n\t\t// Consecutive todo calls collapse into one run like read/find's `explore`\n\t\t// group; the overlay already carries the live list, so the transcript only\n\t\t// needs the sequence of operations.\n\t\ttoolGroup: TODO_TOOL_NAME,\n\n\t\tasync execute(_toolCallId, params, _signal, _onUpdate, ctx): Promise<AgentToolResult<TodoDetails>> {\n\t\t\t// Re-point the active bucket: resume//tree can switch sessions between\n\t\t\t// lifecycle events and this call.\n\t\t\tsetActiveTodoSession(ctx.sessionManager.getSessionId());\n\t\t\tconst result = applyTodoMutation(getTodoState(), params);\n\t\t\tif (result.operation.kind === \"error\") {\n\t\t\t\tthrow new Error(result.operation.message);\n\t\t\t}\n\t\t\tcommitTodoState(result.state);\n\t\t\tconst text = formatTodoContent(result.operation, result.state);\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\tdetails: buildTodoDetails(params, result.state),\n\t\t\t};\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(\n\t\t\t\tcontext.toolGroupSummary\n\t\t\t\t\t? formatTodoGroupCall(args, theme, context)\n\t\t\t\t\t: formatTodoCall(args, theme, context.expanded),\n\t\t\t);\n\t\t\treturn text;\n\t\t},\n\t});\n\n\tpi.registerCommand(TODOS_COMMAND_NAME, {\n\t\tdescription: \"Show todos for the current conversation branch\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tif (!ctx.hasUI) {\n\t\t\t\tctx.ui.notify(\"/todos requires an interactive UI.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsetActiveTodoSession(ctx.sessionManager.getSessionId());\n\t\t\tctx.ui.notify(formatCommandList(getTodoState()), \"info\");\n\t\t},\n\t});\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tsafeReplay(ctx);\n\t\tif (ctx.hasUI) {\n\t\t\toverlay ??= new TodoOverlay();\n\t\t\toverlay.setUI(ctx.ui);\n\t\t\toverlay.resetVisibility();\n\t\t\toverlay.update();\n\t\t}\n\t});\n\n\tpi.on(\"session_compact\", async (_event, ctx) => {\n\t\tsafeReplay(ctx);\n\t\toverlay?.resetVisibility();\n\t\toverlay?.update();\n\t});\n\n\tpi.on(\"session_tree\", async (_event, ctx) => {\n\t\tsafeReplay(ctx);\n\t\toverlay?.resetVisibility();\n\t\toverlay?.update();\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tdisposeTodoSession(ctx.sessionManager.getSessionId());\n\t\toverlay?.dispose();\n\t\toverlay = undefined;\n\t});\n\n\tpi.on(\"tool_execution_end\", async (event) => {\n\t\tif (event.toolName !== TODO_TOOL_NAME || event.isError) return;\n\t\toverlay?.update();\n\t});\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Static, Type } from "typebox";
|
|
2
2
|
export type TodoStatus = "pending" | "in_progress" | "completed" | "deleted";
|
|
3
|
-
export type TodoAction = "create" | "update" | "list" | "get" | "delete" | "clear";
|
|
3
|
+
export type TodoAction = "create" | "create_many" | "update" | "list" | "get" | "delete" | "clear";
|
|
4
4
|
export interface TodoItem {
|
|
5
5
|
id: number;
|
|
6
6
|
subject: string;
|
|
@@ -15,26 +15,78 @@ export interface TodoState {
|
|
|
15
15
|
items: TodoItem[];
|
|
16
16
|
nextId: number;
|
|
17
17
|
}
|
|
18
|
+
/** Current shape of snapshots written to todo tool results. */
|
|
19
|
+
export declare const TODO_DETAILS_SCHEMA_VERSION = 1;
|
|
20
|
+
export type TodoOperationSummary = {
|
|
21
|
+
kind: "create";
|
|
22
|
+
ids: number[];
|
|
23
|
+
} | {
|
|
24
|
+
kind: "create_many";
|
|
25
|
+
ids: number[];
|
|
26
|
+
} | {
|
|
27
|
+
kind: "update";
|
|
28
|
+
id: number;
|
|
29
|
+
status: TodoStatus;
|
|
30
|
+
} | {
|
|
31
|
+
kind: "list";
|
|
32
|
+
status?: TodoStatus;
|
|
33
|
+
includeDeleted: boolean;
|
|
34
|
+
limit?: number;
|
|
35
|
+
afterId?: number;
|
|
36
|
+
query?: string;
|
|
37
|
+
unblockedOnly: boolean;
|
|
38
|
+
/** Exact status counts for the returned page, not the full snapshot. */
|
|
39
|
+
statusCounts?: Partial<Record<TodoStatus, number>>;
|
|
40
|
+
resultCount?: number;
|
|
41
|
+
} | {
|
|
42
|
+
kind: "get";
|
|
43
|
+
id: number;
|
|
44
|
+
} | {
|
|
45
|
+
kind: "delete";
|
|
46
|
+
id: number;
|
|
47
|
+
} | {
|
|
48
|
+
kind: "clear";
|
|
49
|
+
count: number;
|
|
50
|
+
};
|
|
18
51
|
export interface TodoDetails {
|
|
52
|
+
schemaVersion: typeof TODO_DETAILS_SCHEMA_VERSION;
|
|
19
53
|
action: TodoAction;
|
|
20
54
|
params: Record<string, unknown>;
|
|
55
|
+
operation: TodoOperationSummary;
|
|
21
56
|
items: TodoItem[];
|
|
22
57
|
nextId: number;
|
|
23
|
-
error?: string;
|
|
24
58
|
}
|
|
25
59
|
export declare const TodoParamsSchema: Type.TObject<{
|
|
26
|
-
action: Type.TUnsafe<"clear" | "create" | "delete" | "get" | "list" | "update">;
|
|
60
|
+
action: Type.TUnsafe<"clear" | "create" | "create_many" | "delete" | "get" | "list" | "update">;
|
|
27
61
|
subject: Type.TOptional<Type.TString>;
|
|
28
62
|
description: Type.TOptional<Type.TString>;
|
|
29
63
|
activeForm: Type.TOptional<Type.TString>;
|
|
30
64
|
status: Type.TOptional<Type.TUnsafe<"completed" | "deleted" | "in_progress" | "pending">>;
|
|
31
|
-
blockedBy: Type.TOptional<Type.TArray<Type.
|
|
32
|
-
|
|
33
|
-
|
|
65
|
+
blockedBy: Type.TOptional<Type.TArray<Type.TInteger>>;
|
|
66
|
+
items: Type.TOptional<Type.TArray<Type.TObject<{
|
|
67
|
+
key: Type.TOptional<Type.TString>;
|
|
68
|
+
subject: Type.TString;
|
|
69
|
+
description: Type.TString;
|
|
70
|
+
activeForm: Type.TOptional<Type.TString>;
|
|
71
|
+
blockedBy: Type.TOptional<Type.TArray<Type.TInteger>>;
|
|
72
|
+
blockedByKeys: Type.TOptional<Type.TArray<Type.TString>>;
|
|
73
|
+
owner: Type.TOptional<Type.TString>;
|
|
74
|
+
metadata: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
|
|
75
|
+
}>>>;
|
|
76
|
+
addBlockedBy: Type.TOptional<Type.TArray<Type.TInteger>>;
|
|
77
|
+
removeBlockedBy: Type.TOptional<Type.TArray<Type.TInteger>>;
|
|
78
|
+
addBlocks: Type.TOptional<Type.TArray<Type.TInteger>>;
|
|
79
|
+
removeBlocks: Type.TOptional<Type.TArray<Type.TInteger>>;
|
|
34
80
|
owner: Type.TOptional<Type.TString>;
|
|
35
81
|
metadata: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
|
|
36
|
-
id: Type.TOptional<Type.
|
|
82
|
+
id: Type.TOptional<Type.TInteger>;
|
|
37
83
|
includeDeleted: Type.TOptional<Type.TBoolean>;
|
|
84
|
+
limit: Type.TOptional<Type.TInteger>;
|
|
85
|
+
afterId: Type.TOptional<Type.TInteger>;
|
|
86
|
+
query: Type.TOptional<Type.TString>;
|
|
87
|
+
unblockedOnly: Type.TOptional<Type.TBoolean>;
|
|
88
|
+
confirm: Type.TOptional<Type.TBoolean>;
|
|
89
|
+
expectedCount: Type.TOptional<Type.TInteger>;
|
|
38
90
|
}>;
|
|
39
91
|
export type TodoParams = Static<typeof TodoParamsSchema>;
|
|
40
92
|
export declare const EMPTY_TODO_STATE: TodoState;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/extensions/todo/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;AAC7E,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEnF,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,SAAS;IACzB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAYD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;EAyC3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEzD,eAAO,MAAM,gBAAgB,EAAE,SAAoC,CAAC","sourcesContent":["import { StringEnum } from \"@earendil-works/pi-ai\";\nimport { type Static, Type } from \"typebox\";\n\nexport type TodoStatus = \"pending\" | \"in_progress\" | \"completed\" | \"deleted\";\nexport type TodoAction = \"create\" | \"update\" | \"list\" | \"get\" | \"delete\" | \"clear\";\n\nexport interface TodoItem {\n\tid: number;\n\tsubject: string;\n\tdescription?: string;\n\tactiveForm?: string;\n\tstatus: TodoStatus;\n\tblockedBy?: number[];\n\towner?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface TodoState {\n\titems: TodoItem[];\n\tnextId: number;\n}\n\nexport interface TodoDetails {\n\taction: TodoAction;\n\tparams: Record<string, unknown>;\n\titems: TodoItem[];\n\tnextId: number;\n\terror?: string;\n}\n\nconst StatusSchema = StringEnum([\"pending\", \"in_progress\", \"completed\", \"deleted\"] as const, {\n\tdescription:\n\t\t\"Task status: pending for future work, in_progress for the single active task, completed for verified done work, and deleted for an immutable obsolete-task tombstone. Set deleted through update or use the delete action. With action list, status also acts as a filter: only tasks with this status are listed.\",\n});\n\nconst ActionSchema = StringEnum([\"create\", \"update\", \"list\", \"get\", \"delete\", \"clear\"] as const, {\n\tdescription:\n\t\t\"Todo operation: create a pending task, update status/details/dependencies, list current tasks (optionally filtered by status), get one task, delete an obsolete task and release dependents, or clear the list without reusing ids.\",\n});\n\nexport const TodoParamsSchema = Type.Object({\n\taction: ActionSchema,\n\tsubject: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Short imperative task subject, required for create; use a reviewable unit of work, not a micro-step.\",\n\t\t}),\n\t),\n\tdescription: Type.Optional(\n\t\tType.String({ description: \"Longer task notes, acceptance criteria, or verification detail.\" }),\n\t),\n\tactiveForm: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Present-continuous label shown while in_progress, such as 'reading code' or 'updating prompts'.\",\n\t\t}),\n\t),\n\tstatus: Type.Optional(StatusSchema),\n\tblockedBy: Type.Optional(\n\t\tType.Array(Type.Number(), {\n\t\t\tdescription: \"Initial dependency ids for create; use only for real ordering constraints.\",\n\t\t}),\n\t),\n\taddBlockedBy: Type.Optional(\n\t\tType.Array(Type.Number(), {\n\t\t\tdescription: \"Dependency ids to add on update; dependencies must exist and cannot create cycles.\",\n\t\t}),\n\t),\n\tremoveBlockedBy: Type.Optional(Type.Array(Type.Number(), { description: \"Dependency ids to remove on update.\" })),\n\towner: Type.Optional(Type.String({ description: \"Optional owner or agent label for multi-agent coordination.\" })),\n\tmetadata: Type.Optional(\n\t\tType.Record(Type.String(), Type.Unknown(), {\n\t\t\tdescription: \"Optional structured metadata for integrations; null values delete keys on update.\",\n\t\t}),\n\t),\n\tid: Type.Optional(Type.Number({ description: \"Task id, required for update, get, and delete.\" })),\n\tincludeDeleted: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Include deleted tombstones in unfiltered list output; an explicit status=deleted query includes them automatically.\",\n\t\t}),\n\t),\n});\n\nexport type TodoParams = Static<typeof TodoParamsSchema>;\n\nexport const EMPTY_TODO_STATE: TodoState = { items: [], nextId: 1 };\n"]}
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/extensions/todo/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAe5C,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;AAC7E,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,aAAa,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEnG,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,SAAS;IACzB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CACf;AAED,+DAA+D;AAC/D,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAC7B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GAClD;IACA,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,wEAAwE;IACxE,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;CACpB,GACD;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC3B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpC,MAAM,WAAW,WAAW;IAC3B,aAAa,EAAE,OAAO,2BAA2B,CAAC;IAClD,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,SAAS,EAAE,oBAAoB,CAAC;IAChC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CACf;AAgDD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyF3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEzD,eAAO,MAAM,gBAAgB,EAAE,SAAoC,CAAC","sourcesContent":["import { StringEnum } from \"@earendil-works/pi-ai\";\nimport { type Static, Type } from \"typebox\";\nimport {\n\tTODO_MAX_ACTIVE_FORM_LENGTH,\n\tTODO_MAX_BATCH_ITEMS,\n\tTODO_MAX_BATCH_KEY_LENGTH,\n\tTODO_MAX_BLOCKED_BY,\n\tTODO_MAX_DESCRIPTION_LENGTH,\n\tTODO_MAX_LIST_LIMIT,\n\tTODO_MAX_LIST_QUERY_LENGTH,\n\tTODO_MAX_METADATA_ENTRIES,\n\tTODO_MAX_METADATA_KEY_LENGTH,\n\tTODO_MAX_OWNER_LENGTH,\n\tTODO_MAX_SUBJECT_LENGTH,\n} from \"./constants.ts\";\n\nexport type TodoStatus = \"pending\" | \"in_progress\" | \"completed\" | \"deleted\";\nexport type TodoAction = \"create\" | \"create_many\" | \"update\" | \"list\" | \"get\" | \"delete\" | \"clear\";\n\nexport interface TodoItem {\n\tid: number;\n\tsubject: string;\n\tdescription?: string;\n\tactiveForm?: string;\n\tstatus: TodoStatus;\n\tblockedBy?: number[];\n\towner?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface TodoState {\n\titems: TodoItem[];\n\tnextId: number;\n}\n\n/** Current shape of snapshots written to todo tool results. */\nexport const TODO_DETAILS_SCHEMA_VERSION = 1;\n\nexport type TodoOperationSummary =\n\t| { kind: \"create\"; ids: number[] }\n\t| { kind: \"create_many\"; ids: number[] }\n\t| { kind: \"update\"; id: number; status: TodoStatus }\n\t| {\n\t\t\tkind: \"list\";\n\t\t\tstatus?: TodoStatus;\n\t\t\tincludeDeleted: boolean;\n\t\t\tlimit?: number;\n\t\t\tafterId?: number;\n\t\t\tquery?: string;\n\t\t\tunblockedOnly: boolean;\n\t\t\t/** Exact status counts for the returned page, not the full snapshot. */\n\t\t\tstatusCounts?: Partial<Record<TodoStatus, number>>;\n\t\t\tresultCount?: number;\n\t }\n\t| { kind: \"get\"; id: number }\n\t| { kind: \"delete\"; id: number }\n\t| { kind: \"clear\"; count: number };\n\nexport interface TodoDetails {\n\tschemaVersion: typeof TODO_DETAILS_SCHEMA_VERSION;\n\taction: TodoAction;\n\tparams: Record<string, unknown>;\n\toperation: TodoOperationSummary;\n\titems: TodoItem[];\n\tnextId: number;\n}\n\nconst StatusSchema = StringEnum([\"pending\", \"in_progress\", \"completed\", \"deleted\"] as const, {\n\tdescription:\n\t\t\"Task status: pending for future work, in_progress for the single active task, completed for verified done work, and deleted for an immutable obsolete-task tombstone. Set deleted through update or use the delete action. With action list, status also acts as a filter: only tasks with this status are listed.\",\n});\n\nconst ActionSchema = StringEnum([\"create\", \"create_many\", \"update\", \"list\", \"get\", \"delete\", \"clear\"] as const, {\n\tdescription:\n\t\t\"Todo operation: create one pending task, create_many pending tasks atomically, update status/details/dependencies, list current tasks, get one task, delete an obsolete task, or clear the list with confirmation. Parameters that do not apply to the chosen action are rejected.\",\n});\n\nconst TaskId = (description: string) => Type.Integer({ minimum: 1, description });\n\nconst MetadataSchema = Type.Record(Type.String({ maxLength: TODO_MAX_METADATA_KEY_LENGTH }), Type.Unknown(), {\n\tmaxProperties: TODO_MAX_METADATA_ENTRIES,\n\tdescription: \"Structured JSON-like metadata. Values and total payload are validated by the tool.\",\n});\n\nconst CreateManyItemSchema = Type.Object({\n\tkey: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: TODO_MAX_BATCH_KEY_LENGTH,\n\t\t\tdescription: \"Optional unique batch key used by blockedByKeys references.\",\n\t\t}),\n\t),\n\tsubject: Type.String({\n\t\tmaxLength: TODO_MAX_SUBJECT_LENGTH,\n\t\tdescription: \"Short imperative task subject.\",\n\t}),\n\tdescription: Type.String({\n\t\tmaxLength: TODO_MAX_DESCRIPTION_LENGTH,\n\t\tdescription: \"What done means for this task.\",\n\t}),\n\tactiveForm: Type.Optional(Type.String({ maxLength: TODO_MAX_ACTIVE_FORM_LENGTH })),\n\tblockedBy: Type.Optional(\n\t\tType.Array(TaskId(\"Existing task id this task depends on.\"), { maxItems: TODO_MAX_BLOCKED_BY }),\n\t),\n\tblockedByKeys: Type.Optional(\n\t\tType.Array(Type.String({ maxLength: TODO_MAX_BATCH_KEY_LENGTH }), {\n\t\t\tmaxItems: TODO_MAX_BLOCKED_BY,\n\t\t\tdescription: \"Keys of tasks in this create_many batch that must complete first.\",\n\t\t}),\n\t),\n\towner: Type.Optional(Type.String({ maxLength: TODO_MAX_OWNER_LENGTH })),\n\tmetadata: Type.Optional(MetadataSchema),\n});\n\nexport const TodoParamsSchema = Type.Object({\n\taction: ActionSchema,\n\tsubject: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: TODO_MAX_SUBJECT_LENGTH,\n\t\t\tdescription:\n\t\t\t\t\"Short imperative task subject, required for create; use a reviewable unit of work, not a micro-step.\",\n\t\t}),\n\t),\n\tdescription: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: TODO_MAX_DESCRIPTION_LENGTH,\n\t\t\tdescription:\n\t\t\t\t\"What done means for this task: notes, acceptance criteria, or verification detail. Required for create; on update an empty string removes it.\",\n\t\t}),\n\t),\n\tactiveForm: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: TODO_MAX_ACTIVE_FORM_LENGTH,\n\t\t\tdescription:\n\t\t\t\t\"Present-continuous label shown while in_progress, such as 'reading code' or 'updating prompts'; on update an empty string removes it.\",\n\t\t}),\n\t),\n\tstatus: Type.Optional(StatusSchema),\n\tblockedBy: Type.Optional(\n\t\tType.Array(TaskId(\"Existing task id this task depends on.\"), {\n\t\t\tmaxItems: TODO_MAX_BLOCKED_BY,\n\t\t\tdescription:\n\t\t\t\t\"Initial dependency ids, create only (update rejects it; use addBlockedBy/removeBlockedBy there); use only for real ordering constraints.\",\n\t\t}),\n\t),\n\titems: Type.Optional(\n\t\tType.Array(CreateManyItemSchema, {\n\t\t\tminItems: 1,\n\t\t\tmaxItems: TODO_MAX_BATCH_ITEMS,\n\t\t\tdescription:\n\t\t\t\t\"create_many only: up to 20 pending tasks, created atomically in input order. Use key and blockedByKeys for dependencies within this batch.\",\n\t\t}),\n\t),\n\taddBlockedBy: Type.Optional(\n\t\tType.Array(TaskId(\"Existing task id that must complete first.\"), {\n\t\t\tmaxItems: TODO_MAX_BLOCKED_BY,\n\t\t\tdescription: \"Dependency ids to add on update; dependencies must exist and cannot create cycles.\",\n\t\t}),\n\t),\n\tremoveBlockedBy: Type.Optional(Type.Array(TaskId(\"Dependency id to detach.\"), { maxItems: TODO_MAX_BLOCKED_BY })),\n\taddBlocks: Type.Optional(\n\t\tType.Array(TaskId(\"Existing task id that must wait for this one.\"), {\n\t\t\tmaxItems: TODO_MAX_BLOCKED_BY,\n\t\t\tdescription:\n\t\t\t\t\"Update only: ids of tasks that cannot start until this task completes (the reverse of addBlockedBy); targets must exist and cannot create cycles.\",\n\t\t}),\n\t),\n\tremoveBlocks: Type.Optional(Type.Array(TaskId(\"Dependent task id to release.\"), { maxItems: TODO_MAX_BLOCKED_BY })),\n\towner: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: TODO_MAX_OWNER_LENGTH,\n\t\t\tdescription:\n\t\t\t\t\"Optional owner or agent label for multi-agent coordination; on update an empty string removes it.\",\n\t\t}),\n\t),\n\tmetadata: Type.Optional(\n\t\tType.Record(Type.String({ maxLength: TODO_MAX_METADATA_KEY_LENGTH }), Type.Unknown(), {\n\t\t\tmaxProperties: TODO_MAX_METADATA_ENTRIES,\n\t\t\tdescription: \"Optional bounded JSON-like structured metadata; null values delete keys on update.\",\n\t\t}),\n\t),\n\tid: Type.Optional(TaskId(\"Task id, required for update, get, and delete.\")),\n\tincludeDeleted: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Include deleted tombstones in unfiltered list output; an explicit status=deleted query includes them automatically.\",\n\t\t}),\n\t),\n\tlimit: Type.Optional(\n\t\tType.Integer({ minimum: 1, maximum: TODO_MAX_LIST_LIMIT, description: \"List page size (1 through 50).\" }),\n\t),\n\tafterId: Type.Optional(TaskId(\"List only tasks with ids greater than this id.\")),\n\tquery: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: TODO_MAX_LIST_QUERY_LENGTH,\n\t\t\tdescription: \"Case-insensitive subject and description search for list.\",\n\t\t}),\n\t),\n\tunblockedOnly: Type.Optional(Type.Boolean({ description: \"List only tasks with no unresolved dependencies.\" })),\n\tconfirm: Type.Optional(Type.Boolean({ description: \"clear only: must be true.\" })),\n\texpectedCount: Type.Optional(\n\t\tType.Integer({ minimum: 0, description: \"clear only: exact current task count required as a safety check.\" }),\n\t),\n});\n\nexport type TodoParams = Static<typeof TodoParamsSchema>;\n\nexport const EMPTY_TODO_STATE: TodoState = { items: [], nextId: 1 };\n"]}
|
|
@@ -1,36 +1,96 @@
|
|
|
1
1
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
|
+
import { TODO_MAX_ACTIVE_FORM_LENGTH, TODO_MAX_BATCH_ITEMS, TODO_MAX_BATCH_KEY_LENGTH, TODO_MAX_BLOCKED_BY, TODO_MAX_DESCRIPTION_LENGTH, TODO_MAX_LIST_LIMIT, TODO_MAX_LIST_QUERY_LENGTH, TODO_MAX_METADATA_ENTRIES, TODO_MAX_METADATA_KEY_LENGTH, TODO_MAX_OWNER_LENGTH, TODO_MAX_SUBJECT_LENGTH, } from "./constants.js";
|
|
4
|
+
/** Current shape of snapshots written to todo tool results. */
|
|
5
|
+
export const TODO_DETAILS_SCHEMA_VERSION = 1;
|
|
3
6
|
const StatusSchema = StringEnum(["pending", "in_progress", "completed", "deleted"], {
|
|
4
7
|
description: "Task status: pending for future work, in_progress for the single active task, completed for verified done work, and deleted for an immutable obsolete-task tombstone. Set deleted through update or use the delete action. With action list, status also acts as a filter: only tasks with this status are listed.",
|
|
5
8
|
});
|
|
6
|
-
const ActionSchema = StringEnum(["create", "update", "list", "get", "delete", "clear"], {
|
|
7
|
-
description: "Todo operation: create
|
|
9
|
+
const ActionSchema = StringEnum(["create", "create_many", "update", "list", "get", "delete", "clear"], {
|
|
10
|
+
description: "Todo operation: create one pending task, create_many pending tasks atomically, update status/details/dependencies, list current tasks, get one task, delete an obsolete task, or clear the list with confirmation. Parameters that do not apply to the chosen action are rejected.",
|
|
11
|
+
});
|
|
12
|
+
const TaskId = (description) => Type.Integer({ minimum: 1, description });
|
|
13
|
+
const MetadataSchema = Type.Record(Type.String({ maxLength: TODO_MAX_METADATA_KEY_LENGTH }), Type.Unknown(), {
|
|
14
|
+
maxProperties: TODO_MAX_METADATA_ENTRIES,
|
|
15
|
+
description: "Structured JSON-like metadata. Values and total payload are validated by the tool.",
|
|
16
|
+
});
|
|
17
|
+
const CreateManyItemSchema = Type.Object({
|
|
18
|
+
key: Type.Optional(Type.String({
|
|
19
|
+
maxLength: TODO_MAX_BATCH_KEY_LENGTH,
|
|
20
|
+
description: "Optional unique batch key used by blockedByKeys references.",
|
|
21
|
+
})),
|
|
22
|
+
subject: Type.String({
|
|
23
|
+
maxLength: TODO_MAX_SUBJECT_LENGTH,
|
|
24
|
+
description: "Short imperative task subject.",
|
|
25
|
+
}),
|
|
26
|
+
description: Type.String({
|
|
27
|
+
maxLength: TODO_MAX_DESCRIPTION_LENGTH,
|
|
28
|
+
description: "What done means for this task.",
|
|
29
|
+
}),
|
|
30
|
+
activeForm: Type.Optional(Type.String({ maxLength: TODO_MAX_ACTIVE_FORM_LENGTH })),
|
|
31
|
+
blockedBy: Type.Optional(Type.Array(TaskId("Existing task id this task depends on."), { maxItems: TODO_MAX_BLOCKED_BY })),
|
|
32
|
+
blockedByKeys: Type.Optional(Type.Array(Type.String({ maxLength: TODO_MAX_BATCH_KEY_LENGTH }), {
|
|
33
|
+
maxItems: TODO_MAX_BLOCKED_BY,
|
|
34
|
+
description: "Keys of tasks in this create_many batch that must complete first.",
|
|
35
|
+
})),
|
|
36
|
+
owner: Type.Optional(Type.String({ maxLength: TODO_MAX_OWNER_LENGTH })),
|
|
37
|
+
metadata: Type.Optional(MetadataSchema),
|
|
8
38
|
});
|
|
9
39
|
export const TodoParamsSchema = Type.Object({
|
|
10
40
|
action: ActionSchema,
|
|
11
41
|
subject: Type.Optional(Type.String({
|
|
42
|
+
maxLength: TODO_MAX_SUBJECT_LENGTH,
|
|
12
43
|
description: "Short imperative task subject, required for create; use a reviewable unit of work, not a micro-step.",
|
|
13
44
|
})),
|
|
14
|
-
description: Type.Optional(Type.String({
|
|
45
|
+
description: Type.Optional(Type.String({
|
|
46
|
+
maxLength: TODO_MAX_DESCRIPTION_LENGTH,
|
|
47
|
+
description: "What done means for this task: notes, acceptance criteria, or verification detail. Required for create; on update an empty string removes it.",
|
|
48
|
+
})),
|
|
15
49
|
activeForm: Type.Optional(Type.String({
|
|
16
|
-
|
|
50
|
+
maxLength: TODO_MAX_ACTIVE_FORM_LENGTH,
|
|
51
|
+
description: "Present-continuous label shown while in_progress, such as 'reading code' or 'updating prompts'; on update an empty string removes it.",
|
|
17
52
|
})),
|
|
18
53
|
status: Type.Optional(StatusSchema),
|
|
19
|
-
blockedBy: Type.Optional(Type.Array(
|
|
20
|
-
|
|
54
|
+
blockedBy: Type.Optional(Type.Array(TaskId("Existing task id this task depends on."), {
|
|
55
|
+
maxItems: TODO_MAX_BLOCKED_BY,
|
|
56
|
+
description: "Initial dependency ids, create only (update rejects it; use addBlockedBy/removeBlockedBy there); use only for real ordering constraints.",
|
|
21
57
|
})),
|
|
22
|
-
|
|
58
|
+
items: Type.Optional(Type.Array(CreateManyItemSchema, {
|
|
59
|
+
minItems: 1,
|
|
60
|
+
maxItems: TODO_MAX_BATCH_ITEMS,
|
|
61
|
+
description: "create_many only: up to 20 pending tasks, created atomically in input order. Use key and blockedByKeys for dependencies within this batch.",
|
|
62
|
+
})),
|
|
63
|
+
addBlockedBy: Type.Optional(Type.Array(TaskId("Existing task id that must complete first."), {
|
|
64
|
+
maxItems: TODO_MAX_BLOCKED_BY,
|
|
23
65
|
description: "Dependency ids to add on update; dependencies must exist and cannot create cycles.",
|
|
24
66
|
})),
|
|
25
|
-
removeBlockedBy: Type.Optional(Type.Array(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
description: "
|
|
67
|
+
removeBlockedBy: Type.Optional(Type.Array(TaskId("Dependency id to detach."), { maxItems: TODO_MAX_BLOCKED_BY })),
|
|
68
|
+
addBlocks: Type.Optional(Type.Array(TaskId("Existing task id that must wait for this one."), {
|
|
69
|
+
maxItems: TODO_MAX_BLOCKED_BY,
|
|
70
|
+
description: "Update only: ids of tasks that cannot start until this task completes (the reverse of addBlockedBy); targets must exist and cannot create cycles.",
|
|
71
|
+
})),
|
|
72
|
+
removeBlocks: Type.Optional(Type.Array(TaskId("Dependent task id to release."), { maxItems: TODO_MAX_BLOCKED_BY })),
|
|
73
|
+
owner: Type.Optional(Type.String({
|
|
74
|
+
maxLength: TODO_MAX_OWNER_LENGTH,
|
|
75
|
+
description: "Optional owner or agent label for multi-agent coordination; on update an empty string removes it.",
|
|
29
76
|
})),
|
|
30
|
-
|
|
77
|
+
metadata: Type.Optional(Type.Record(Type.String({ maxLength: TODO_MAX_METADATA_KEY_LENGTH }), Type.Unknown(), {
|
|
78
|
+
maxProperties: TODO_MAX_METADATA_ENTRIES,
|
|
79
|
+
description: "Optional bounded JSON-like structured metadata; null values delete keys on update.",
|
|
80
|
+
})),
|
|
81
|
+
id: Type.Optional(TaskId("Task id, required for update, get, and delete.")),
|
|
31
82
|
includeDeleted: Type.Optional(Type.Boolean({
|
|
32
83
|
description: "Include deleted tombstones in unfiltered list output; an explicit status=deleted query includes them automatically.",
|
|
33
84
|
})),
|
|
85
|
+
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: TODO_MAX_LIST_LIMIT, description: "List page size (1 through 50)." })),
|
|
86
|
+
afterId: Type.Optional(TaskId("List only tasks with ids greater than this id.")),
|
|
87
|
+
query: Type.Optional(Type.String({
|
|
88
|
+
maxLength: TODO_MAX_LIST_QUERY_LENGTH,
|
|
89
|
+
description: "Case-insensitive subject and description search for list.",
|
|
90
|
+
})),
|
|
91
|
+
unblockedOnly: Type.Optional(Type.Boolean({ description: "List only tasks with no unresolved dependencies." })),
|
|
92
|
+
confirm: Type.Optional(Type.Boolean({ description: "clear only: must be true." })),
|
|
93
|
+
expectedCount: Type.Optional(Type.Integer({ minimum: 0, description: "clear only: exact current task count required as a safety check." })),
|
|
34
94
|
});
|
|
35
95
|
export const EMPTY_TODO_STATE = { items: [], nextId: 1 };
|
|
36
96
|
//# sourceMappingURL=schema.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/extensions/todo/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAe,IAAI,EAAE,MAAM,SAAS,CAAC;AA6B5C,MAAM,YAAY,GAAG,UAAU,CAAC,CAAC,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,CAAU,EAAE;IAC5F,WAAW,EACV,oTAAoT;CACrT,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,UAAU,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAU,EAAE;IAChG,WAAW,EACV,qOAAqO;CACtO,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC;IAC3C,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,IAAI,CAAC,QAAQ,CACrB,IAAI,CAAC,MAAM,CAAC;QACX,WAAW,EACV,sGAAsG;KACvG,CAAC,CACF;IACD,WAAW,EAAE,IAAI,CAAC,QAAQ,CACzB,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,iEAAiE,EAAE,CAAC,CAC/F;IACD,UAAU,EAAE,IAAI,CAAC,QAAQ,CACxB,IAAI,CAAC,MAAM,CAAC;QACX,WAAW,EAAE,iGAAiG;KAC9G,CAAC,CACF;IACD,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;IACnC,SAAS,EAAE,IAAI,CAAC,QAAQ,CACvB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE;QACzB,WAAW,EAAE,4EAA4E;KACzF,CAAC,CACF;IACD,YAAY,EAAE,IAAI,CAAC,QAAQ,CAC1B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE;QACzB,WAAW,EAAE,oFAAoF;KACjG,CAAC,CACF;IACD,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC,CAAC;IACjH,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,6DAA6D,EAAE,CAAC,CAAC;IACjH,QAAQ,EAAE,IAAI,CAAC,QAAQ,CACtB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE;QAC1C,WAAW,EAAE,mFAAmF;KAChG,CAAC,CACF;IACD,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,gDAAgD,EAAE,CAAC,CAAC;IACjG,cAAc,EAAE,IAAI,CAAC,QAAQ,CAC5B,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EACV,qHAAqH;KACtH,CAAC,CACF;CACD,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,gBAAgB,GAAc,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC","sourcesContent":["import { StringEnum } from \"@earendil-works/pi-ai\";\nimport { type Static, Type } from \"typebox\";\n\nexport type TodoStatus = \"pending\" | \"in_progress\" | \"completed\" | \"deleted\";\nexport type TodoAction = \"create\" | \"update\" | \"list\" | \"get\" | \"delete\" | \"clear\";\n\nexport interface TodoItem {\n\tid: number;\n\tsubject: string;\n\tdescription?: string;\n\tactiveForm?: string;\n\tstatus: TodoStatus;\n\tblockedBy?: number[];\n\towner?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface TodoState {\n\titems: TodoItem[];\n\tnextId: number;\n}\n\nexport interface TodoDetails {\n\taction: TodoAction;\n\tparams: Record<string, unknown>;\n\titems: TodoItem[];\n\tnextId: number;\n\terror?: string;\n}\n\nconst StatusSchema = StringEnum([\"pending\", \"in_progress\", \"completed\", \"deleted\"] as const, {\n\tdescription:\n\t\t\"Task status: pending for future work, in_progress for the single active task, completed for verified done work, and deleted for an immutable obsolete-task tombstone. Set deleted through update or use the delete action. With action list, status also acts as a filter: only tasks with this status are listed.\",\n});\n\nconst ActionSchema = StringEnum([\"create\", \"update\", \"list\", \"get\", \"delete\", \"clear\"] as const, {\n\tdescription:\n\t\t\"Todo operation: create a pending task, update status/details/dependencies, list current tasks (optionally filtered by status), get one task, delete an obsolete task and release dependents, or clear the list without reusing ids.\",\n});\n\nexport const TodoParamsSchema = Type.Object({\n\taction: ActionSchema,\n\tsubject: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Short imperative task subject, required for create; use a reviewable unit of work, not a micro-step.\",\n\t\t}),\n\t),\n\tdescription: Type.Optional(\n\t\tType.String({ description: \"Longer task notes, acceptance criteria, or verification detail.\" }),\n\t),\n\tactiveForm: Type.Optional(\n\t\tType.String({\n\t\t\tdescription: \"Present-continuous label shown while in_progress, such as 'reading code' or 'updating prompts'.\",\n\t\t}),\n\t),\n\tstatus: Type.Optional(StatusSchema),\n\tblockedBy: Type.Optional(\n\t\tType.Array(Type.Number(), {\n\t\t\tdescription: \"Initial dependency ids for create; use only for real ordering constraints.\",\n\t\t}),\n\t),\n\taddBlockedBy: Type.Optional(\n\t\tType.Array(Type.Number(), {\n\t\t\tdescription: \"Dependency ids to add on update; dependencies must exist and cannot create cycles.\",\n\t\t}),\n\t),\n\tremoveBlockedBy: Type.Optional(Type.Array(Type.Number(), { description: \"Dependency ids to remove on update.\" })),\n\towner: Type.Optional(Type.String({ description: \"Optional owner or agent label for multi-agent coordination.\" })),\n\tmetadata: Type.Optional(\n\t\tType.Record(Type.String(), Type.Unknown(), {\n\t\t\tdescription: \"Optional structured metadata for integrations; null values delete keys on update.\",\n\t\t}),\n\t),\n\tid: Type.Optional(Type.Number({ description: \"Task id, required for update, get, and delete.\" })),\n\tincludeDeleted: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Include deleted tombstones in unfiltered list output; an explicit status=deleted query includes them automatically.\",\n\t\t}),\n\t),\n});\n\nexport type TodoParams = Static<typeof TodoParamsSchema>;\n\nexport const EMPTY_TODO_STATE: TodoState = { items: [], nextId: 1 };\n"]}
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/extensions/todo/schema.ts"],"names":[],"mappings":"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","sourcesContent":["import { StringEnum } from \"@earendil-works/pi-ai\";\nimport { type Static, Type } from \"typebox\";\nimport {\n\tTODO_MAX_ACTIVE_FORM_LENGTH,\n\tTODO_MAX_BATCH_ITEMS,\n\tTODO_MAX_BATCH_KEY_LENGTH,\n\tTODO_MAX_BLOCKED_BY,\n\tTODO_MAX_DESCRIPTION_LENGTH,\n\tTODO_MAX_LIST_LIMIT,\n\tTODO_MAX_LIST_QUERY_LENGTH,\n\tTODO_MAX_METADATA_ENTRIES,\n\tTODO_MAX_METADATA_KEY_LENGTH,\n\tTODO_MAX_OWNER_LENGTH,\n\tTODO_MAX_SUBJECT_LENGTH,\n} from \"./constants.ts\";\n\nexport type TodoStatus = \"pending\" | \"in_progress\" | \"completed\" | \"deleted\";\nexport type TodoAction = \"create\" | \"create_many\" | \"update\" | \"list\" | \"get\" | \"delete\" | \"clear\";\n\nexport interface TodoItem {\n\tid: number;\n\tsubject: string;\n\tdescription?: string;\n\tactiveForm?: string;\n\tstatus: TodoStatus;\n\tblockedBy?: number[];\n\towner?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface TodoState {\n\titems: TodoItem[];\n\tnextId: number;\n}\n\n/** Current shape of snapshots written to todo tool results. */\nexport const TODO_DETAILS_SCHEMA_VERSION = 1;\n\nexport type TodoOperationSummary =\n\t| { kind: \"create\"; ids: number[] }\n\t| { kind: \"create_many\"; ids: number[] }\n\t| { kind: \"update\"; id: number; status: TodoStatus }\n\t| {\n\t\t\tkind: \"list\";\n\t\t\tstatus?: TodoStatus;\n\t\t\tincludeDeleted: boolean;\n\t\t\tlimit?: number;\n\t\t\tafterId?: number;\n\t\t\tquery?: string;\n\t\t\tunblockedOnly: boolean;\n\t\t\t/** Exact status counts for the returned page, not the full snapshot. */\n\t\t\tstatusCounts?: Partial<Record<TodoStatus, number>>;\n\t\t\tresultCount?: number;\n\t }\n\t| { kind: \"get\"; id: number }\n\t| { kind: \"delete\"; id: number }\n\t| { kind: \"clear\"; count: number };\n\nexport interface TodoDetails {\n\tschemaVersion: typeof TODO_DETAILS_SCHEMA_VERSION;\n\taction: TodoAction;\n\tparams: Record<string, unknown>;\n\toperation: TodoOperationSummary;\n\titems: TodoItem[];\n\tnextId: number;\n}\n\nconst StatusSchema = StringEnum([\"pending\", \"in_progress\", \"completed\", \"deleted\"] as const, {\n\tdescription:\n\t\t\"Task status: pending for future work, in_progress for the single active task, completed for verified done work, and deleted for an immutable obsolete-task tombstone. Set deleted through update or use the delete action. With action list, status also acts as a filter: only tasks with this status are listed.\",\n});\n\nconst ActionSchema = StringEnum([\"create\", \"create_many\", \"update\", \"list\", \"get\", \"delete\", \"clear\"] as const, {\n\tdescription:\n\t\t\"Todo operation: create one pending task, create_many pending tasks atomically, update status/details/dependencies, list current tasks, get one task, delete an obsolete task, or clear the list with confirmation. Parameters that do not apply to the chosen action are rejected.\",\n});\n\nconst TaskId = (description: string) => Type.Integer({ minimum: 1, description });\n\nconst MetadataSchema = Type.Record(Type.String({ maxLength: TODO_MAX_METADATA_KEY_LENGTH }), Type.Unknown(), {\n\tmaxProperties: TODO_MAX_METADATA_ENTRIES,\n\tdescription: \"Structured JSON-like metadata. Values and total payload are validated by the tool.\",\n});\n\nconst CreateManyItemSchema = Type.Object({\n\tkey: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: TODO_MAX_BATCH_KEY_LENGTH,\n\t\t\tdescription: \"Optional unique batch key used by blockedByKeys references.\",\n\t\t}),\n\t),\n\tsubject: Type.String({\n\t\tmaxLength: TODO_MAX_SUBJECT_LENGTH,\n\t\tdescription: \"Short imperative task subject.\",\n\t}),\n\tdescription: Type.String({\n\t\tmaxLength: TODO_MAX_DESCRIPTION_LENGTH,\n\t\tdescription: \"What done means for this task.\",\n\t}),\n\tactiveForm: Type.Optional(Type.String({ maxLength: TODO_MAX_ACTIVE_FORM_LENGTH })),\n\tblockedBy: Type.Optional(\n\t\tType.Array(TaskId(\"Existing task id this task depends on.\"), { maxItems: TODO_MAX_BLOCKED_BY }),\n\t),\n\tblockedByKeys: Type.Optional(\n\t\tType.Array(Type.String({ maxLength: TODO_MAX_BATCH_KEY_LENGTH }), {\n\t\t\tmaxItems: TODO_MAX_BLOCKED_BY,\n\t\t\tdescription: \"Keys of tasks in this create_many batch that must complete first.\",\n\t\t}),\n\t),\n\towner: Type.Optional(Type.String({ maxLength: TODO_MAX_OWNER_LENGTH })),\n\tmetadata: Type.Optional(MetadataSchema),\n});\n\nexport const TodoParamsSchema = Type.Object({\n\taction: ActionSchema,\n\tsubject: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: TODO_MAX_SUBJECT_LENGTH,\n\t\t\tdescription:\n\t\t\t\t\"Short imperative task subject, required for create; use a reviewable unit of work, not a micro-step.\",\n\t\t}),\n\t),\n\tdescription: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: TODO_MAX_DESCRIPTION_LENGTH,\n\t\t\tdescription:\n\t\t\t\t\"What done means for this task: notes, acceptance criteria, or verification detail. Required for create; on update an empty string removes it.\",\n\t\t}),\n\t),\n\tactiveForm: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: TODO_MAX_ACTIVE_FORM_LENGTH,\n\t\t\tdescription:\n\t\t\t\t\"Present-continuous label shown while in_progress, such as 'reading code' or 'updating prompts'; on update an empty string removes it.\",\n\t\t}),\n\t),\n\tstatus: Type.Optional(StatusSchema),\n\tblockedBy: Type.Optional(\n\t\tType.Array(TaskId(\"Existing task id this task depends on.\"), {\n\t\t\tmaxItems: TODO_MAX_BLOCKED_BY,\n\t\t\tdescription:\n\t\t\t\t\"Initial dependency ids, create only (update rejects it; use addBlockedBy/removeBlockedBy there); use only for real ordering constraints.\",\n\t\t}),\n\t),\n\titems: Type.Optional(\n\t\tType.Array(CreateManyItemSchema, {\n\t\t\tminItems: 1,\n\t\t\tmaxItems: TODO_MAX_BATCH_ITEMS,\n\t\t\tdescription:\n\t\t\t\t\"create_many only: up to 20 pending tasks, created atomically in input order. Use key and blockedByKeys for dependencies within this batch.\",\n\t\t}),\n\t),\n\taddBlockedBy: Type.Optional(\n\t\tType.Array(TaskId(\"Existing task id that must complete first.\"), {\n\t\t\tmaxItems: TODO_MAX_BLOCKED_BY,\n\t\t\tdescription: \"Dependency ids to add on update; dependencies must exist and cannot create cycles.\",\n\t\t}),\n\t),\n\tremoveBlockedBy: Type.Optional(Type.Array(TaskId(\"Dependency id to detach.\"), { maxItems: TODO_MAX_BLOCKED_BY })),\n\taddBlocks: Type.Optional(\n\t\tType.Array(TaskId(\"Existing task id that must wait for this one.\"), {\n\t\t\tmaxItems: TODO_MAX_BLOCKED_BY,\n\t\t\tdescription:\n\t\t\t\t\"Update only: ids of tasks that cannot start until this task completes (the reverse of addBlockedBy); targets must exist and cannot create cycles.\",\n\t\t}),\n\t),\n\tremoveBlocks: Type.Optional(Type.Array(TaskId(\"Dependent task id to release.\"), { maxItems: TODO_MAX_BLOCKED_BY })),\n\towner: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: TODO_MAX_OWNER_LENGTH,\n\t\t\tdescription:\n\t\t\t\t\"Optional owner or agent label for multi-agent coordination; on update an empty string removes it.\",\n\t\t}),\n\t),\n\tmetadata: Type.Optional(\n\t\tType.Record(Type.String({ maxLength: TODO_MAX_METADATA_KEY_LENGTH }), Type.Unknown(), {\n\t\t\tmaxProperties: TODO_MAX_METADATA_ENTRIES,\n\t\t\tdescription: \"Optional bounded JSON-like structured metadata; null values delete keys on update.\",\n\t\t}),\n\t),\n\tid: Type.Optional(TaskId(\"Task id, required for update, get, and delete.\")),\n\tincludeDeleted: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Include deleted tombstones in unfiltered list output; an explicit status=deleted query includes them automatically.\",\n\t\t}),\n\t),\n\tlimit: Type.Optional(\n\t\tType.Integer({ minimum: 1, maximum: TODO_MAX_LIST_LIMIT, description: \"List page size (1 through 50).\" }),\n\t),\n\tafterId: Type.Optional(TaskId(\"List only tasks with ids greater than this id.\")),\n\tquery: Type.Optional(\n\t\tType.String({\n\t\t\tmaxLength: TODO_MAX_LIST_QUERY_LENGTH,\n\t\t\tdescription: \"Case-insensitive subject and description search for list.\",\n\t\t}),\n\t),\n\tunblockedOnly: Type.Optional(Type.Boolean({ description: \"List only tasks with no unresolved dependencies.\" })),\n\tconfirm: Type.Optional(Type.Boolean({ description: \"clear only: must be true.\" })),\n\texpectedCount: Type.Optional(\n\t\tType.Integer({ minimum: 0, description: \"clear only: exact current task count required as a safety check.\" }),\n\t),\n});\n\nexport type TodoParams = Static<typeof TodoParamsSchema>;\n\nexport const EMPTY_TODO_STATE: TodoState = { items: [], nextId: 1 };\n"]}
|
|
@@ -2,6 +2,9 @@ import { type TodoDetails, type TodoItem, type TodoParams, type TodoState, type
|
|
|
2
2
|
type Operation = {
|
|
3
3
|
kind: "create";
|
|
4
4
|
id: number;
|
|
5
|
+
} | {
|
|
6
|
+
kind: "create_many";
|
|
7
|
+
ids: number[];
|
|
5
8
|
} | {
|
|
6
9
|
kind: "update";
|
|
7
10
|
id: number;
|
|
@@ -9,19 +12,30 @@ type Operation = {
|
|
|
9
12
|
to: TodoStatus;
|
|
10
13
|
/** Other tasks auto-demoted from in_progress → pending to keep exactly one active. */
|
|
11
14
|
demotedIds?: number[];
|
|
15
|
+
/** Active tasks demoted because a new dependency is still incomplete. */
|
|
16
|
+
blockedIds?: number[];
|
|
17
|
+
/** Pending dependents left fully unblocked by an update to status deleted. */
|
|
18
|
+
releasedIds?: number[];
|
|
12
19
|
/** Soft note when the list is fully closed without a verification-style task. */
|
|
13
20
|
verificationNudge?: boolean;
|
|
14
|
-
} | {
|
|
15
|
-
kind: "delete";
|
|
16
|
-
id: number;
|
|
17
|
-
subject: string;
|
|
18
21
|
} | {
|
|
19
22
|
kind: "list";
|
|
20
23
|
status?: TodoStatus;
|
|
21
24
|
includeDeleted: boolean;
|
|
25
|
+
limit: number;
|
|
26
|
+
afterId?: number;
|
|
27
|
+
query?: string;
|
|
28
|
+
unblockedOnly: boolean;
|
|
29
|
+
/** Keep the historical no-options list text stable. */
|
|
30
|
+
legacyOutput: boolean;
|
|
22
31
|
} | {
|
|
23
32
|
kind: "get";
|
|
24
33
|
item: TodoItem;
|
|
34
|
+
} | {
|
|
35
|
+
kind: "delete";
|
|
36
|
+
id: number;
|
|
37
|
+
subject: string;
|
|
38
|
+
releasedIds?: number[];
|
|
25
39
|
} | {
|
|
26
40
|
kind: "clear";
|
|
27
41
|
count: number;
|
|
@@ -35,6 +49,7 @@ export interface MutationResult {
|
|
|
35
49
|
}
|
|
36
50
|
/** Point the module at a session's state bucket. Call wherever ctx is in hand. */
|
|
37
51
|
export declare function setActiveTodoSession(sid: string): void;
|
|
52
|
+
/** Snapshot of the active session's state; mutate via applyTodoMutation + commitTodoState. */
|
|
38
53
|
export declare function getTodoState(): TodoState;
|
|
39
54
|
export declare function replaceTodoState(next: TodoState): void;
|
|
40
55
|
export declare function commitTodoState(next: TodoState): void;
|
|
@@ -51,7 +66,7 @@ export declare function replayTodosFromBranch(ctx: {
|
|
|
51
66
|
getBranch(): Iterable<unknown>;
|
|
52
67
|
};
|
|
53
68
|
}): TodoState;
|
|
54
|
-
export declare function buildTodoDetails(params: TodoParams, next: TodoState
|
|
69
|
+
export declare function buildTodoDetails(params: TodoParams, next: TodoState): TodoDetails;
|
|
55
70
|
export declare function formatTodoContent(operation: Operation, state: TodoState): string;
|
|
56
71
|
export {};
|
|
57
72
|
//# sourceMappingURL=state.d.ts.map
|