@agimon-ai/po-task 0.0.0-alpha.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.
Files changed (161) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/dist/apiContracts.cjs +54 -0
  4. package/dist/apiContracts.cjs.map +1 -0
  5. package/dist/apiContracts.d.mts +53 -0
  6. package/dist/apiContracts.d.mts.map +1 -0
  7. package/dist/apiContracts.mjs +49 -0
  8. package/dist/apiContracts.mjs.map +1 -0
  9. package/dist/config-CFSs3iku.mjs +72 -0
  10. package/dist/config-CFSs3iku.mjs.map +1 -0
  11. package/dist/config-CdX-Bv8q.cjs +173 -0
  12. package/dist/config-CdX-Bv8q.cjs.map +1 -0
  13. package/dist/config.cjs +19 -0
  14. package/dist/config.d.mts +2 -0
  15. package/dist/config.mjs +2 -0
  16. package/dist/delegation-BiOy3_7g.mjs +7 -0
  17. package/dist/delegation-BiOy3_7g.mjs.map +1 -0
  18. package/dist/delegation-CdMqOZB4.mjs +626 -0
  19. package/dist/delegation-CdMqOZB4.mjs.map +1 -0
  20. package/dist/delegation-DsnKjMbJ.cjs +661 -0
  21. package/dist/delegation-DsnKjMbJ.cjs.map +1 -0
  22. package/dist/delegation-LDpHP2CC.cjs +12 -0
  23. package/dist/delegation-LDpHP2CC.cjs.map +1 -0
  24. package/dist/delegationManager.cjs +10 -0
  25. package/dist/delegationManager.d.mts +2 -0
  26. package/dist/delegationManager.mjs +3 -0
  27. package/dist/extensions/mcp-ui.mjs +23 -0
  28. package/dist/extensions/mcp-ui.mjs.map +1 -0
  29. package/dist/extensions/mcp.cjs +1144 -0
  30. package/dist/extensions/mcp.cjs.map +1 -0
  31. package/dist/extensions/mcp.d.mts +5 -0
  32. package/dist/extensions/mcp.d.mts.map +1 -0
  33. package/dist/extensions/mcp.mjs +1115 -0
  34. package/dist/extensions/mcp.mjs.map +1 -0
  35. package/dist/extensions/native.mjs +1175 -0
  36. package/dist/extensions/native.mjs.map +1 -0
  37. package/dist/extensions/server.cjs +569 -0
  38. package/dist/extensions/server.cjs.map +1 -0
  39. package/dist/extensions/server.d.mts +5 -0
  40. package/dist/extensions/server.d.mts.map +1 -0
  41. package/dist/extensions/server.mjs +563 -0
  42. package/dist/extensions/server.mjs.map +1 -0
  43. package/dist/extensions/web.mjs +1018 -0
  44. package/dist/extensions/web.mjs.map +1 -0
  45. package/dist/index-B-3sCF2S.d.mts +43 -0
  46. package/dist/index-B-3sCF2S.d.mts.map +1 -0
  47. package/dist/index-B1JPJdpZ.d.mts +51 -0
  48. package/dist/index-B1JPJdpZ.d.mts.map +1 -0
  49. package/dist/index-CUWCvh-E.d.mts +72 -0
  50. package/dist/index-CUWCvh-E.d.mts.map +1 -0
  51. package/dist/index-DlmXKyhQ.d.mts +34 -0
  52. package/dist/index-DlmXKyhQ.d.mts.map +1 -0
  53. package/dist/index-Dvb8X_qB.d.mts +67 -0
  54. package/dist/index-Dvb8X_qB.d.mts.map +1 -0
  55. package/dist/index-XY9VPmfm.d.mts +26 -0
  56. package/dist/index-XY9VPmfm.d.mts.map +1 -0
  57. package/dist/index-teIf67qQ.d.mts +196 -0
  58. package/dist/index-teIf67qQ.d.mts.map +1 -0
  59. package/dist/index.cjs +93 -0
  60. package/dist/index.d.mts +11 -0
  61. package/dist/index.mjs +12 -0
  62. package/dist/invariants-DhjEjBPU.cjs +79 -0
  63. package/dist/invariants-DhjEjBPU.cjs.map +1 -0
  64. package/dist/invariants-q_UamF7D.mjs +62 -0
  65. package/dist/invariants-q_UamF7D.mjs.map +1 -0
  66. package/dist/logSinkTelemetry-B23qGiP-.cjs +52 -0
  67. package/dist/logSinkTelemetry-B23qGiP-.cjs.map +1 -0
  68. package/dist/logSinkTelemetry-BMBsC54p.mjs +41 -0
  69. package/dist/logSinkTelemetry-BMBsC54p.mjs.map +1 -0
  70. package/dist/logSinkTelemetry.cjs +6 -0
  71. package/dist/logSinkTelemetry.d.mts +23 -0
  72. package/dist/logSinkTelemetry.d.mts.map +1 -0
  73. package/dist/logSinkTelemetry.mjs +3 -0
  74. package/dist/paths-CSya2l9_.cjs +308 -0
  75. package/dist/paths-CSya2l9_.cjs.map +1 -0
  76. package/dist/paths-Cah1vFGv.mjs +207 -0
  77. package/dist/paths-Cah1vFGv.mjs.map +1 -0
  78. package/dist/processLiveness-Ccd9wenT.mjs +20 -0
  79. package/dist/processLiveness-Ccd9wenT.mjs.map +1 -0
  80. package/dist/processLiveness-umpNgsPR.cjs +25 -0
  81. package/dist/processLiveness-umpNgsPR.cjs.map +1 -0
  82. package/dist/reconcile-BcD-hTbd.mjs +63 -0
  83. package/dist/reconcile-BcD-hTbd.mjs.map +1 -0
  84. package/dist/reconcile-cRL0NjDr.cjs +80 -0
  85. package/dist/reconcile-cRL0NjDr.cjs.map +1 -0
  86. package/dist/reducer-BEfJFM5L.mjs +345 -0
  87. package/dist/reducer-BEfJFM5L.mjs.map +1 -0
  88. package/dist/reducer-BV0YIZ2o.cjs +380 -0
  89. package/dist/reducer-BV0YIZ2o.cjs.map +1 -0
  90. package/dist/storeInvariants.cjs +5 -0
  91. package/dist/storeInvariants.d.mts +23 -0
  92. package/dist/storeInvariants.d.mts.map +1 -0
  93. package/dist/storeInvariants.mjs +2 -0
  94. package/dist/storePaths.cjs +14 -0
  95. package/dist/storePaths.d.mts +2 -0
  96. package/dist/storePaths.mjs +2 -0
  97. package/dist/storeProcessLiveness.cjs +3 -0
  98. package/dist/storeProcessLiveness.d.mts +11 -0
  99. package/dist/storeProcessLiveness.d.mts.map +1 -0
  100. package/dist/storeProcessLiveness.mjs +2 -0
  101. package/dist/storeReconcile.cjs +5 -0
  102. package/dist/storeReconcile.d.mts +2 -0
  103. package/dist/storeReconcile.mjs +2 -0
  104. package/dist/storeReducer.cjs +8 -0
  105. package/dist/storeReducer.d.mts +2 -0
  106. package/dist/storeReducer.mjs +2 -0
  107. package/dist/storeTaskGraph.cjs +7 -0
  108. package/dist/storeTaskGraph.d.mts +2 -0
  109. package/dist/storeTaskGraph.mjs +2 -0
  110. package/dist/storeTaskStore.cjs +3 -0
  111. package/dist/storeTaskStore.d.mts +2 -0
  112. package/dist/storeTaskStore.mjs +2 -0
  113. package/dist/storeTypes.cjs +6 -0
  114. package/dist/storeTypes.d.mts +2 -0
  115. package/dist/storeTypes.mjs +2 -0
  116. package/dist/task-CXF9VULl.cjs +49 -0
  117. package/dist/task-CXF9VULl.cjs.map +1 -0
  118. package/dist/task-DJpTteeJ.d.mts +187 -0
  119. package/dist/task-DJpTteeJ.d.mts.map +1 -0
  120. package/dist/task-DO0TwZ0z.mjs +132 -0
  121. package/dist/task-DO0TwZ0z.mjs.map +1 -0
  122. package/dist/task-Dhta7JyA.d.mts +126 -0
  123. package/dist/task-Dhta7JyA.d.mts.map +1 -0
  124. package/dist/task-UvW877Ez.cjs +227 -0
  125. package/dist/task-UvW877Ez.cjs.map +1 -0
  126. package/dist/task-giCbpKzV.mjs +26 -0
  127. package/dist/task-giCbpKzV.mjs.map +1 -0
  128. package/dist/taskGraph-BOQvT0-M.mjs +63 -0
  129. package/dist/taskGraph-BOQvT0-M.mjs.map +1 -0
  130. package/dist/taskGraph-BskRslrr.cjs +92 -0
  131. package/dist/taskGraph-BskRslrr.cjs.map +1 -0
  132. package/dist/taskGraph-D0zFHF2p.d.mts +25 -0
  133. package/dist/taskGraph-D0zFHF2p.d.mts.map +1 -0
  134. package/dist/taskResult-D2G7b4w-.cjs +224 -0
  135. package/dist/taskResult-D2G7b4w-.cjs.map +1 -0
  136. package/dist/taskResult-edBAAKSH.mjs +165 -0
  137. package/dist/taskResult-edBAAKSH.mjs.map +1 -0
  138. package/dist/taskStore-DjU60rMm.cjs +215 -0
  139. package/dist/taskStore-DjU60rMm.cjs.map +1 -0
  140. package/dist/taskStore-pAJ971Ml.mjs +208 -0
  141. package/dist/taskStore-pAJ971Ml.mjs.map +1 -0
  142. package/dist/telemetry-Ca0I3Tqo.cjs +30 -0
  143. package/dist/telemetry-Ca0I3Tqo.cjs.map +1 -0
  144. package/dist/telemetry-DyS44zAD.mjs +25 -0
  145. package/dist/telemetry-DyS44zAD.mjs.map +1 -0
  146. package/dist/telemetry-FMGRovb4.d.mts +35 -0
  147. package/dist/telemetry-FMGRovb4.d.mts.map +1 -0
  148. package/dist/toolPromptGuidelines.cjs +27 -0
  149. package/dist/toolPromptGuidelines.cjs.map +1 -0
  150. package/dist/toolPromptGuidelines.d.mts +13 -0
  151. package/dist/toolPromptGuidelines.d.mts.map +1 -0
  152. package/dist/toolPromptGuidelines.mjs +26 -0
  153. package/dist/toolPromptGuidelines.mjs.map +1 -0
  154. package/dist/toolResponseEnvelope.cjs +12 -0
  155. package/dist/toolResponseEnvelope.d.mts +2 -0
  156. package/dist/toolResponseEnvelope.mjs +2 -0
  157. package/dist/toolSchema.cjs +18 -0
  158. package/dist/toolSchema.d.mts +2 -0
  159. package/dist/toolSchema.mjs +2 -0
  160. package/package.json +223 -0
  161. package/src/types/webTasks.ts +24 -0
@@ -0,0 +1,227 @@
1
+ require("./delegation-LDpHP2CC.cjs");
2
+ let _agimon_ai_po_core_delegation = require("@agimon-ai/po-core/delegation");
3
+ let typebox = require("typebox");
4
+ //#region src/constants/task.ts
5
+ const COMMAND_NAME = "tasks";
6
+ //#endregion
7
+ //#region src/schemas/task.ts
8
+ const TASK_ACTIONS = [
9
+ "upsert",
10
+ "list",
11
+ "get",
12
+ "delete",
13
+ "clear",
14
+ "assign",
15
+ "cancel"
16
+ ];
17
+ const TASK_STATUSES = [
18
+ "pending",
19
+ "in_progress",
20
+ "completed",
21
+ "failed",
22
+ "deleted"
23
+ ];
24
+ const TASK_CONTEXTS = ["fresh", "fork"];
25
+ const TOOL_NAME = "task";
26
+ const TOOL_LABEL = "Task";
27
+ const ERR_REQUIRES_INTERACTIVE = "/tasks requires interactive mode";
28
+ const MSG_NO_TASKS = "No tasks yet. Ask the agent to add some!";
29
+ /**
30
+ * Which top-level fields each action accepts.
31
+ *
32
+ * The params object is flat and shared across every action, so the schema alone
33
+ * cannot express "upsert has no top-level id". That gap matters: a call like
34
+ * `{"action":"upsert","id":3,"tasks":[{"status":"completed"}]}` would otherwise
35
+ * silently create a duplicate task, because the entry itself carries no id.
36
+ */
37
+ const TASK_ACTION_FIELDS = {
38
+ upsert: ["action", "tasks"],
39
+ list: [
40
+ "action",
41
+ "status",
42
+ "includeDeleted"
43
+ ],
44
+ get: ["action", "id"],
45
+ delete: ["action", "id"],
46
+ clear: ["action"],
47
+ assign: ["action", "assignments"],
48
+ cancel: ["action", "id"]
49
+ };
50
+ function taskActionAcceptsField(action, field) {
51
+ return TASK_ACTION_FIELDS[action].includes(field);
52
+ }
53
+ /** Correction hint appended when a per-task field lands at the top level. */
54
+ const MSG_UPSERT_FIELD_MISPLACED = "Per-task fields belong inside each tasks[] entry, not at the top level: {\"action\":\"upsert\",\"tasks\":[{\"id\":3,\"status\":\"completed\"}]}.";
55
+ /**
56
+ * A dependency target: an existing task id, or the `ref` of a task created
57
+ * earlier in the same call.
58
+ */
59
+ const DepTokenSchema = typebox.Type.Union([typebox.Type.Integer(), typebox.Type.String()]);
60
+ /**
61
+ * One entry of an upsert. `additionalProperties: false` is load-bearing: pi
62
+ * validates tool arguments against this schema before `execute` runs, so a
63
+ * stray `{"action":"update"}` inside an entry is rejected at the boundary.
64
+ */
65
+ const TaskItemSchema = typebox.Type.Object({
66
+ id: typebox.Type.Optional(typebox.Type.Integer({ description: "Existing task id to change. Omit to create. An unknown id fails only that entry; it never creates." })),
67
+ ref: typebox.Type.Optional(typebox.Type.String({ description: "Temporary name for a task created by this entry, so a LATER entry in this array can list it in blockedBy. Must start with a letter; never stored." })),
68
+ subject: typebox.Type.Optional(typebox.Type.String({ description: "Short imperative subject. Required when the entry has no id; on an entry with an id it renames." })),
69
+ description: typebox.Type.Optional(typebox.Type.String({ description: "Long-form detail; becomes the brief handed to the subagent on assign." })),
70
+ activeForm: typebox.Type.Optional(typebox.Type.String({ description: "Present-continuous label shown while in_progress (e.g. 'writing tests')" })),
71
+ status: typebox.Type.Optional(typebox.Type.String({
72
+ enum: [...TASK_STATUSES],
73
+ description: "New entries default to pending and cannot start deleted; on an entry with an id the transition must be legal."
74
+ })),
75
+ blockedBy: typebox.Type.Optional(typebox.Type.Array(DepTokenSchema, { description: "Dependencies for a NEW entry: task ids, or refs of tasks created EARLIER in this array. On an entry with an id use addBlockedBy / removeBlockedBy instead." })),
76
+ addBlockedBy: typebox.Type.Optional(typebox.Type.Array(DepTokenSchema, { description: "Dependencies to add (ids or earlier refs). Additive; do not resend the full array." })),
77
+ removeBlockedBy: typebox.Type.Optional(typebox.Type.Array(DepTokenSchema, { description: "Dependencies to remove. Additive; do not resend the full array." })),
78
+ owner: typebox.Type.Optional(typebox.Type.String({ description: "Owner label; assign sets it to the subagent name." })),
79
+ metadata: typebox.Type.Optional(typebox.Type.Record(typebox.Type.String(), typebox.Type.Unknown(), { description: "Merged into the task; a null value deletes that key." }))
80
+ }, { additionalProperties: false });
81
+ /** One task-to-agent handoff in a native assignment batch. */
82
+ const TaskAssignmentSchema = typebox.Type.Object({
83
+ id: typebox.Type.Integer({ description: "Pending, unblocked task id to delegate" }),
84
+ agent: typebox.Type.String({ description: "Exact discovered subagent name" }),
85
+ inlineAgent: typebox.Type.Optional(_agimon_ai_po_core_delegation.InlineAgentSchema),
86
+ instructions: typebox.Type.Optional(typebox.Type.String({ description: "Extra instructions appended to this delegated brief" })),
87
+ relevantFiles: typebox.Type.Optional(typebox.Type.Array(typebox.Type.String(), { description: `Files already read or located, relative to the working directory; at most 12 are used.` })),
88
+ priorFindings: typebox.Type.Optional(typebox.Type.String({ description: "Established facts this child should consume rather than re-derive" })),
89
+ model: typebox.Type.Optional(typebox.Type.String({ description: "Model override for this delegated run" })),
90
+ context: typebox.Type.Optional(typebox.Type.String({
91
+ enum: [...TASK_CONTEXTS],
92
+ description: "Starting context for this child: 'fresh' or 'fork'"
93
+ }))
94
+ }, { additionalProperties: false });
95
+ /**
96
+ * Tool parameters. Every `description` doubles as LLM-facing prompt copy, so
97
+ * wording changes here change model behaviour.
98
+ */
99
+ const TaskParamsSchema = typebox.Type.Object({
100
+ action: typebox.Type.String({ enum: [...TASK_ACTIONS] }),
101
+ tasks: typebox.Type.Optional(typebox.Type.Array(TaskItemSchema, {
102
+ minItems: 1,
103
+ description: "upsert only. An entry with an id changes that task, an entry without an id creates one. Entries apply in array order, so a later entry can depend on an earlier one by ref."
104
+ })),
105
+ assignments: typebox.Type.Optional(typebox.Type.Array(TaskAssignmentSchema, {
106
+ minItems: 1,
107
+ description: "Required for assign, one entry per task, including when there is only one."
108
+ })),
109
+ id: typebox.Type.Optional(typebox.Type.Integer({ description: "Task id, for get, delete and cancel. Not accepted by upsert or assign: those carry ids inside tasks[] and assignments[] entries." })),
110
+ status: typebox.Type.Optional(typebox.Type.String({
111
+ enum: [...TASK_STATUSES],
112
+ description: "list only: return just the tasks in this status. To SET a status, upsert the task by id."
113
+ })),
114
+ includeDeleted: typebox.Type.Optional(typebox.Type.Boolean({ description: "list only: also return deleted tombstones. Default false." }))
115
+ });
116
+ /** Remote MCP connections track tasks only; delegation stays with the local agent. */
117
+ const REMOTE_TASK_ACTIONS = [
118
+ "upsert",
119
+ "list",
120
+ "get",
121
+ "delete",
122
+ "clear"
123
+ ];
124
+ const { assignments: _assignments, ...remoteTaskProperties } = TaskParamsSchema.properties;
125
+ const RemoteTaskParamsSchema = typebox.Type.Object({
126
+ ...remoteTaskProperties,
127
+ action: typebox.Type.String({ enum: [...REMOTE_TASK_ACTIONS] })
128
+ });
129
+ //#endregion
130
+ Object.defineProperty(exports, "COMMAND_NAME", {
131
+ enumerable: true,
132
+ get: function() {
133
+ return COMMAND_NAME;
134
+ }
135
+ });
136
+ Object.defineProperty(exports, "ERR_REQUIRES_INTERACTIVE", {
137
+ enumerable: true,
138
+ get: function() {
139
+ return ERR_REQUIRES_INTERACTIVE;
140
+ }
141
+ });
142
+ Object.defineProperty(exports, "MSG_NO_TASKS", {
143
+ enumerable: true,
144
+ get: function() {
145
+ return MSG_NO_TASKS;
146
+ }
147
+ });
148
+ Object.defineProperty(exports, "MSG_UPSERT_FIELD_MISPLACED", {
149
+ enumerable: true,
150
+ get: function() {
151
+ return MSG_UPSERT_FIELD_MISPLACED;
152
+ }
153
+ });
154
+ Object.defineProperty(exports, "REMOTE_TASK_ACTIONS", {
155
+ enumerable: true,
156
+ get: function() {
157
+ return REMOTE_TASK_ACTIONS;
158
+ }
159
+ });
160
+ Object.defineProperty(exports, "RemoteTaskParamsSchema", {
161
+ enumerable: true,
162
+ get: function() {
163
+ return RemoteTaskParamsSchema;
164
+ }
165
+ });
166
+ Object.defineProperty(exports, "TASK_ACTIONS", {
167
+ enumerable: true,
168
+ get: function() {
169
+ return TASK_ACTIONS;
170
+ }
171
+ });
172
+ Object.defineProperty(exports, "TASK_ACTION_FIELDS", {
173
+ enumerable: true,
174
+ get: function() {
175
+ return TASK_ACTION_FIELDS;
176
+ }
177
+ });
178
+ Object.defineProperty(exports, "TASK_CONTEXTS", {
179
+ enumerable: true,
180
+ get: function() {
181
+ return TASK_CONTEXTS;
182
+ }
183
+ });
184
+ Object.defineProperty(exports, "TASK_STATUSES", {
185
+ enumerable: true,
186
+ get: function() {
187
+ return TASK_STATUSES;
188
+ }
189
+ });
190
+ Object.defineProperty(exports, "TOOL_LABEL", {
191
+ enumerable: true,
192
+ get: function() {
193
+ return TOOL_LABEL;
194
+ }
195
+ });
196
+ Object.defineProperty(exports, "TOOL_NAME", {
197
+ enumerable: true,
198
+ get: function() {
199
+ return TOOL_NAME;
200
+ }
201
+ });
202
+ Object.defineProperty(exports, "TaskAssignmentSchema", {
203
+ enumerable: true,
204
+ get: function() {
205
+ return TaskAssignmentSchema;
206
+ }
207
+ });
208
+ Object.defineProperty(exports, "TaskItemSchema", {
209
+ enumerable: true,
210
+ get: function() {
211
+ return TaskItemSchema;
212
+ }
213
+ });
214
+ Object.defineProperty(exports, "TaskParamsSchema", {
215
+ enumerable: true,
216
+ get: function() {
217
+ return TaskParamsSchema;
218
+ }
219
+ });
220
+ Object.defineProperty(exports, "taskActionAcceptsField", {
221
+ enumerable: true,
222
+ get: function() {
223
+ return taskActionAcceptsField;
224
+ }
225
+ });
226
+
227
+ //# sourceMappingURL=task-UvW877Ez.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-UvW877Ez.cjs","names":["Type","InlineAgentSchema"],"sources":["../src/constants/task.ts","../src/schemas/task.ts"],"sourcesContent":["export const COMMAND_NAME = 'tasks';\n","export { COMMAND_NAME } from '../constants/task';\nimport { InlineAgentSchema } from '@agimon-ai/po-core/delegation';\nimport { type Static, Type } from 'typebox';\n\nimport type { TaskAction } from '../models/task';\nimport { MAX_BRIEF_FILES } from '../types/delegation';\n\nexport const TASK_ACTIONS = ['upsert', 'list', 'get', 'delete', 'clear', 'assign', 'cancel'] as const;\nexport const TASK_STATUSES = ['pending', 'in_progress', 'completed', 'failed', 'deleted'] as const;\nexport const TASK_CONTEXTS = ['fresh', 'fork'] as const;\n\nexport const TOOL_NAME = 'task';\nexport const TOOL_LABEL = 'Task';\n\nexport const ERR_REQUIRES_INTERACTIVE = '/tasks requires interactive mode';\nexport const MSG_NO_TASKS = 'No tasks yet. Ask the agent to add some!';\n\n/**\n * Which top-level fields each action accepts.\n *\n * The params object is flat and shared across every action, so the schema alone\n * cannot express \"upsert has no top-level id\". That gap matters: a call like\n * `{\"action\":\"upsert\",\"id\":3,\"tasks\":[{\"status\":\"completed\"}]}` would otherwise\n * silently create a duplicate task, because the entry itself carries no id.\n */\nexport const TASK_ACTION_FIELDS = {\n upsert: ['action', 'tasks'],\n list: ['action', 'status', 'includeDeleted'],\n get: ['action', 'id'],\n delete: ['action', 'id'],\n clear: ['action'],\n assign: ['action', 'assignments'],\n cancel: ['action', 'id'],\n} as const satisfies Record<TaskAction, readonly string[]>;\n\nexport function taskActionAcceptsField(action: TaskAction, field: string): boolean {\n return (TASK_ACTION_FIELDS[action] as readonly string[]).includes(field);\n}\n\n/** Correction hint appended when a per-task field lands at the top level. */\nexport const MSG_UPSERT_FIELD_MISPLACED =\n 'Per-task fields belong inside each tasks[] entry, not at the top level: {\"action\":\"upsert\",\"tasks\":[{\"id\":3,\"status\":\"completed\"}]}.';\n\n/**\n * A dependency target: an existing task id, or the `ref` of a task created\n * earlier in the same call.\n */\nconst DepTokenSchema = Type.Union([Type.Integer(), Type.String()]);\n\n/**\n * One entry of an upsert. `additionalProperties: false` is load-bearing: pi\n * validates tool arguments against this schema before `execute` runs, so a\n * stray `{\"action\":\"update\"}` inside an entry is rejected at the boundary.\n */\nexport const TaskItemSchema = Type.Object(\n {\n id: Type.Optional(\n Type.Integer({\n description:\n 'Existing task id to change. Omit to create. An unknown id fails only that entry; it never creates.',\n }),\n ),\n ref: Type.Optional(\n Type.String({\n description:\n 'Temporary name for a task created by this entry, so a LATER entry in this array can list it in blockedBy. Must start with a letter; never stored.',\n }),\n ),\n subject: Type.Optional(\n Type.String({\n description: 'Short imperative subject. Required when the entry has no id; on an entry with an id it renames.',\n }),\n ),\n description: Type.Optional(\n Type.String({ description: 'Long-form detail; becomes the brief handed to the subagent on assign.' }),\n ),\n activeForm: Type.Optional(\n Type.String({ description: \"Present-continuous label shown while in_progress (e.g. 'writing tests')\" }),\n ),\n status: Type.Optional(\n Type.String({\n enum: [...TASK_STATUSES],\n description:\n 'New entries default to pending and cannot start deleted; on an entry with an id the transition must be legal.',\n }),\n ),\n blockedBy: Type.Optional(\n Type.Array(DepTokenSchema, {\n description:\n 'Dependencies for a NEW entry: task ids, or refs of tasks created EARLIER in this array. On an entry with an id use addBlockedBy / removeBlockedBy instead.',\n }),\n ),\n addBlockedBy: Type.Optional(\n Type.Array(DepTokenSchema, {\n description: 'Dependencies to add (ids or earlier refs). Additive; do not resend the full array.',\n }),\n ),\n removeBlockedBy: Type.Optional(\n Type.Array(DepTokenSchema, {\n description: 'Dependencies to remove. Additive; do not resend the full array.',\n }),\n ),\n owner: Type.Optional(Type.String({ description: 'Owner label; assign sets it to the subagent name.' })),\n metadata: Type.Optional(\n Type.Record(Type.String(), Type.Unknown(), {\n description: 'Merged into the task; a null value deletes that key.',\n }),\n ),\n },\n { additionalProperties: false },\n);\n\n/** One task-to-agent handoff in a native assignment batch. */\nexport const TaskAssignmentSchema = Type.Object(\n {\n id: Type.Integer({ description: 'Pending, unblocked task id to delegate' }),\n agent: Type.String({ description: 'Exact discovered subagent name' }),\n inlineAgent: Type.Optional(InlineAgentSchema),\n instructions: Type.Optional(Type.String({ description: 'Extra instructions appended to this delegated brief' })),\n relevantFiles: Type.Optional(\n Type.Array(Type.String(), {\n description: `Files already read or located, relative to the working directory; at most ${MAX_BRIEF_FILES} are used.`,\n }),\n ),\n priorFindings: Type.Optional(\n Type.String({ description: 'Established facts this child should consume rather than re-derive' }),\n ),\n model: Type.Optional(Type.String({ description: 'Model override for this delegated run' })),\n context: Type.Optional(\n Type.String({\n enum: [...TASK_CONTEXTS],\n description: \"Starting context for this child: 'fresh' or 'fork'\",\n }),\n ),\n },\n { additionalProperties: false },\n);\n\n/**\n * Tool parameters. Every `description` doubles as LLM-facing prompt copy, so\n * wording changes here change model behaviour.\n */\nexport const TaskParamsSchema = Type.Object({\n action: Type.String({ enum: [...TASK_ACTIONS] }),\n tasks: Type.Optional(\n Type.Array(TaskItemSchema, {\n minItems: 1,\n description:\n 'upsert only. An entry with an id changes that task, an entry without an id creates one. Entries apply in array order, so a later entry can depend on an earlier one by ref.',\n }),\n ),\n assignments: Type.Optional(\n Type.Array(TaskAssignmentSchema, {\n minItems: 1,\n description: 'Required for assign, one entry per task, including when there is only one.',\n }),\n ),\n id: Type.Optional(\n Type.Integer({\n description:\n 'Task id, for get, delete and cancel. Not accepted by upsert or assign: those carry ids inside tasks[] and assignments[] entries.',\n }),\n ),\n status: Type.Optional(\n Type.String({\n enum: [...TASK_STATUSES],\n description: 'list only: return just the tasks in this status. To SET a status, upsert the task by id.',\n }),\n ),\n includeDeleted: Type.Optional(\n Type.Boolean({ description: 'list only: also return deleted tombstones. Default false.' }),\n ),\n});\n\n/** Remote MCP connections track tasks only; delegation stays with the local agent. */\nexport const REMOTE_TASK_ACTIONS = ['upsert', 'list', 'get', 'delete', 'clear'] as const;\nconst { assignments: _assignments, ...remoteTaskProperties } = TaskParamsSchema.properties;\nexport const RemoteTaskParamsSchema = Type.Object({\n ...remoteTaskProperties,\n action: Type.String({ enum: [...REMOTE_TASK_ACTIONS] }),\n});\n\nexport type TaskItemParams = Static<typeof TaskItemSchema>;\nexport type TaskAssignmentParams = Static<typeof TaskAssignmentSchema>;\nexport type TaskParams = Static<typeof TaskParamsSchema>;\n"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ //#region src/models/task.ts
2
+ const DELETED_STATUS = "deleted";
3
+ const STORE_SCHEMA_VERSION = 1;
4
+ function emptyDocument() {
5
+ return {
6
+ version: 1,
7
+ rev: 0,
8
+ nextId: 1,
9
+ tasks: []
10
+ };
11
+ }
12
+ /**
13
+ * A delegation is "live" while the owning run may still produce a response.
14
+ *
15
+ * Takes the delegation shape rather than `Task` so the cockpit's `WebTask`,
16
+ * which carries the state as a plain string, answers the question the same way
17
+ * the store does.
18
+ */
19
+ function isDelegationActive(task) {
20
+ const state = task.delegation?.state;
21
+ return state === "requested" || state === "running";
22
+ }
23
+ //#endregion
24
+ export { isDelegationActive as i, STORE_SCHEMA_VERSION as n, emptyDocument as r, DELETED_STATUS as t };
25
+
26
+ //# sourceMappingURL=task-giCbpKzV.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-giCbpKzV.mjs","names":[],"sources":["../src/models/task.ts"],"sourcesContent":["/**\n * Domain types for the file-backed task store.\n *\n * The on-disk document is the durable source of truth (unlike rpiv-todo, which\n * replayed state from the transcript). Every field here round-trips through\n * JSON, so nothing may hold non-serializable values.\n */\n\nimport type { InlineAgent } from '@agimon-ai/po-core/delegation';\n\nexport type TaskStatus = 'pending' | 'in_progress' | 'completed' | 'failed' | 'deleted';\n\nexport const DELETED_STATUS: TaskStatus = 'deleted';\n\nexport type TaskAction = 'upsert' | 'list' | 'get' | 'delete' | 'clear' | 'assign' | 'cancel';\n\nexport type DelegationState = 'requested' | 'running' | 'completed' | 'failed' | 'cancelled';\n\n/** Terminal result of a delegated subagent run, copied off the delegation response. */\nexport interface DelegationResult {\n status: string;\n output?: string;\n outputPath?: string;\n sessionFile?: string;\n error?: string;\n durationMs?: number;\n toolCount?: number;\n}\n\n/**\n * Delegation bookkeeping attached to a task that was handed to a subagent.\n *\n * `pid` is the harness process that owns the run: it is the only way to tell an\n * genuinely-running delegation from one orphaned by a harness crash, since the\n * child dies with its parent but the file survives.\n */\nexport interface TaskDelegation {\n requestId: string;\n agent: string;\n state: DelegationState;\n sessionId?: string;\n pid?: number;\n runId?: string;\n model?: string;\n startedAt?: string;\n endedAt?: string;\n result?: DelegationResult;\n}\n\nexport interface Task {\n id: number;\n subject: string;\n description?: string;\n activeForm?: string;\n status: TaskStatus;\n blockedBy?: number[];\n owner?: string;\n metadata?: Record<string, unknown>;\n createdAt?: string;\n updatedAt?: string;\n delegation?: TaskDelegation;\n}\n\nexport const STORE_SCHEMA_VERSION = 1;\n\n/** The complete on-disk document. `rev` increments on every committed write. */\nexport interface TaskDocument {\n version: number;\n rev: number;\n nextId: number;\n tasks: Task[];\n}\n\nexport function emptyDocument(): TaskDocument {\n return { version: STORE_SCHEMA_VERSION, rev: 0, nextId: 1, tasks: [] };\n}\n\n/**\n * A dependency target inside an upsert item: an existing task id, or the `ref`\n * of a task created earlier in the same call.\n *\n * Refs must start with a letter (see `REF_PATTERN`), so a ref is structurally\n * unconfusable with a stringified id and the discrimination is just `typeof`.\n */\nexport type DepToken = number | string;\n\n/**\n * One entry of an `upsert` call. No `id` creates a task; an `id` updates one.\n *\n * `blockedBy` (absolute set) is create-only and `addBlockedBy`/`removeBlockedBy`\n * (additive merge) are update-only, matching the split the two former actions\n * had. A field used against the wrong kind fails that item rather than being\n * silently ignored.\n */\nexport interface TaskItemMutation {\n id?: number;\n ref?: string;\n subject?: string;\n description?: string;\n activeForm?: string;\n status?: TaskStatus;\n blockedBy?: DepToken[];\n addBlockedBy?: DepToken[];\n removeBlockedBy?: DepToken[];\n owner?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * What one upsert item did. `index` is its position in the request array, so a\n * model reading a partial result can tell which entries still need resending.\n *\n * `message` is unprefixed: the `item[N] failed:` framing belongs to the\n * formatter, because the Task Space overlay shows this text on its own.\n */\nexport type UpsertItemOutcome =\n | {\n index: number;\n kind: 'created';\n id: number;\n subject: string;\n status: TaskStatus;\n ref?: string;\n blockedBy?: number[];\n }\n | { index: number; kind: 'updated'; id: number; fromStatus: TaskStatus; toStatus: TaskStatus }\n | { index: number; kind: 'unchanged'; id: number; status: TaskStatus }\n | { index: number; kind: 'failed'; message: string; id?: number; ref?: string };\n\n/** Which tasks an upsert touched, so renderResult names them instead of guessing. */\nexport interface UpsertSummary {\n /** Ids of applied entries, in request order. A failed entry contributes nothing. */\n applied: number[];\n failed: number;\n}\n\n/** One task-to-agent handoff inside a native assignment batch. */\nexport interface TaskAssignment {\n id: number;\n agent: string;\n inlineAgent?: InlineAgent;\n instructions?: string;\n relevantFiles?: string[];\n priorFindings?: string;\n model?: string;\n context?: 'fresh' | 'fork';\n}\n\n/** Which assignment entries started, so renderResult can show one consolidated batch. */\nexport interface AssignmentSummary {\n /** Ids of successful entries, in request order. A failed entry contributes nothing. */\n assigned: number[];\n failed: number;\n}\n\n/**\n * Open-shape input bag the reducer accepts. The index signature lets the\n * runtime pass a TypeBox `Static<typeof TaskParamsSchema>` through without\n * casting each field.\n *\n * Per-task fields live on `tasks[]`, not here: a top-level `subject` or\n * `status` would be ambiguous once one call can carry many tasks.\n */\nexport interface TaskMutationParams {\n [key: string]: unknown;\n tasks?: TaskItemMutation[];\n /** `assign` only: independent task-to-agent handoffs dispatched by one tool call. */\n assignments?: TaskAssignment[];\n /** `list` filter only. To set a status, upsert the task by id. */\n status?: TaskStatus;\n /** `get`, `delete`, and `cancel` only. Assign ids live in `assignments[]`. */\n id?: number;\n includeDeleted?: boolean;\n}\n\n/** Snapshot returned under a tool result's `details` for renderResult. */\nexport interface TaskDetails {\n action: TaskAction;\n params: Record<string, unknown>;\n tasks: Task[];\n nextId: number;\n rev: number;\n error?: string;\n upsert?: UpsertSummary;\n assignment?: AssignmentSummary;\n}\n\n/**\n * A delegation is \"live\" while the owning run may still produce a response.\n *\n * Takes the delegation shape rather than `Task` so the cockpit's `WebTask`,\n * which carries the state as a plain string, answers the question the same way\n * the store does.\n */\nexport function isDelegationActive(task: { delegation?: { state?: string } }): boolean {\n const state = task.delegation?.state;\n return state === 'requested' || state === 'running';\n}\n"],"mappings":";AAYA,MAAa,iBAA6B;AAmD1C,MAAa,uBAAuB;AAUpC,SAAgB,gBAA8B;CAC5C,OAAO;EAAE,SAAA;EAA+B,KAAK;EAAG,QAAQ;EAAG,OAAO,CAAC;CAAE;AACvE;;;;;;;;AAuHA,SAAgB,mBAAmB,MAAoD;CACrF,MAAM,QAAQ,KAAK,YAAY;CAC/B,OAAO,UAAU,eAAe,UAAU;AAC5C"}
@@ -0,0 +1,63 @@
1
+ //#region src/models/taskGraph.ts
2
+ const COMPLETED_STATUS = "completed";
3
+ const DELETED_STATUS = "deleted";
4
+ /** True when a non-empty list has no visible work left to finish. */
5
+ function isTaskListComplete(taskList) {
6
+ const visible = taskList.filter((task) => task.status !== DELETED_STATUS);
7
+ return visible.length > 0 && visible.every((task) => task.status === COMPLETED_STATUS);
8
+ }
9
+ /**
10
+ * Would merging `newBlockedBy` into `taskId`'s dependencies introduce a cycle?
11
+ *
12
+ * Takes the proposed additions explicitly so the reducer can ask the question
13
+ * before mutating anything.
14
+ */
15
+ function detectCycle(taskList, taskId, newBlockedBy) {
16
+ const edges = /* @__PURE__ */ new Map();
17
+ for (const task of taskList) if (task.id === taskId) edges.set(task.id, [.../* @__PURE__ */ new Set([...task.blockedBy ?? [], ...newBlockedBy])]);
18
+ else edges.set(task.id, task.blockedBy ? [...task.blockedBy] : []);
19
+ const visiting = /* @__PURE__ */ new Set();
20
+ const visited = /* @__PURE__ */ new Set();
21
+ const hasCycleFrom = (node) => {
22
+ if (visiting.has(node)) return true;
23
+ if (visited.has(node)) return false;
24
+ visiting.add(node);
25
+ for (const next of edges.get(node) ?? []) if (hasCycleFrom(next)) return true;
26
+ visiting.delete(node);
27
+ visited.add(node);
28
+ return false;
29
+ };
30
+ for (const node of edges.keys()) if (hasCycleFrom(node)) return true;
31
+ return false;
32
+ }
33
+ /** Inverse adjacency: for each task, which tasks list it in their blockedBy. */
34
+ function deriveBlocks(taskList) {
35
+ const blocks = /* @__PURE__ */ new Map();
36
+ for (const task of taskList) for (const dep of task.blockedBy ?? []) {
37
+ const dependents = blocks.get(dep) ?? [];
38
+ dependents.push(task.id);
39
+ blocks.set(dep, dependents);
40
+ }
41
+ return blocks;
42
+ }
43
+ /**
44
+ * Blocking dependencies of a task that are not yet resolved.
45
+ *
46
+ * `completed` clears a dependency; `failed` deliberately does not, so a failed
47
+ * delegation keeps its dependents blocked instead of silently releasing work
48
+ * that was never actually finished.
49
+ */
50
+ function unresolvedBlockers(taskList, task) {
51
+ return (task.blockedBy ?? []).filter((dep) => {
52
+ const blocker = taskList.find((candidate) => candidate.id === dep);
53
+ if (!blocker) return false;
54
+ return blocker.status !== COMPLETED_STATUS && blocker.status !== DELETED_STATUS;
55
+ });
56
+ }
57
+ function isBlocked(taskList, task) {
58
+ return unresolvedBlockers(taskList, task).length > 0;
59
+ }
60
+ //#endregion
61
+ export { unresolvedBlockers as a, isTaskListComplete as i, detectCycle as n, isBlocked as r, deriveBlocks as t };
62
+
63
+ //# sourceMappingURL=taskGraph-BOQvT0-M.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taskGraph-BOQvT0-M.mjs","names":[],"sources":["../src/models/taskGraph.ts"],"sourcesContent":["import type { Task } from './task';\n\nconst COMPLETED_STATUS: Task['status'] = 'completed';\nconst DELETED_STATUS: Task['status'] = 'deleted';\n\n/** True when a non-empty list has no visible work left to finish. */\nexport function isTaskListComplete(taskList: readonly Task[]): boolean {\n const visible = taskList.filter((task) => task.status !== DELETED_STATUS);\n return visible.length > 0 && visible.every((task) => task.status === COMPLETED_STATUS);\n}\n\n/**\n * Would merging `newBlockedBy` into `taskId`'s dependencies introduce a cycle?\n *\n * Takes the proposed additions explicitly so the reducer can ask the question\n * before mutating anything.\n */\nexport function detectCycle(taskList: readonly Task[], taskId: number, newBlockedBy: readonly number[]): boolean {\n const edges = new Map<number, number[]>();\n for (const task of taskList) {\n if (task.id === taskId) {\n edges.set(task.id, [...new Set([...(task.blockedBy ?? []), ...newBlockedBy])]);\n } else {\n edges.set(task.id, task.blockedBy ? [...task.blockedBy] : []);\n }\n }\n\n const visiting = new Set<number>();\n const visited = new Set<number>();\n const hasCycleFrom = (node: number): boolean => {\n if (visiting.has(node)) return true;\n if (visited.has(node)) return false;\n visiting.add(node);\n for (const next of edges.get(node) ?? []) {\n if (hasCycleFrom(next)) return true;\n }\n visiting.delete(node);\n visited.add(node);\n return false;\n };\n\n for (const node of edges.keys()) {\n if (hasCycleFrom(node)) return true;\n }\n return false;\n}\n\n/** Inverse adjacency: for each task, which tasks list it in their blockedBy. */\nexport function deriveBlocks(taskList: readonly Task[]): Map<number, number[]> {\n const blocks = new Map<number, number[]>();\n for (const task of taskList) {\n for (const dep of task.blockedBy ?? []) {\n const dependents = blocks.get(dep) ?? [];\n dependents.push(task.id);\n blocks.set(dep, dependents);\n }\n }\n return blocks;\n}\n\n/**\n * Blocking dependencies of a task that are not yet resolved.\n *\n * `completed` clears a dependency; `failed` deliberately does not, so a failed\n * delegation keeps its dependents blocked instead of silently releasing work\n * that was never actually finished.\n */\nexport function unresolvedBlockers(taskList: readonly Task[], task: Task): number[] {\n return (task.blockedBy ?? []).filter((dep) => {\n const blocker = taskList.find((candidate) => candidate.id === dep);\n if (!blocker) return false;\n return blocker.status !== COMPLETED_STATUS && blocker.status !== DELETED_STATUS;\n });\n}\n\nexport function isBlocked(taskList: readonly Task[], task: Task): boolean {\n return unresolvedBlockers(taskList, task).length > 0;\n}\n"],"mappings":";AAEA,MAAM,mBAAmC;AACzC,MAAM,iBAAiC;;AAGvC,SAAgB,mBAAmB,UAAoC;CACrE,MAAM,UAAU,SAAS,QAAQ,SAAS,KAAK,WAAW,cAAc;CACxE,OAAO,QAAQ,SAAS,KAAK,QAAQ,OAAO,SAAS,KAAK,WAAW,gBAAgB;AACvF;;;;;;;AAQA,SAAgB,YAAY,UAA2B,QAAgB,cAA0C;CAC/G,MAAM,wBAAQ,IAAI,IAAsB;CACxC,KAAK,MAAM,QAAQ,UACjB,IAAI,KAAK,OAAO,QACd,MAAM,IAAI,KAAK,IAAI,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAI,KAAK,aAAa,CAAC,GAAI,GAAG,YAAY,CAAC,CAAC,CAAC;MAE7E,MAAM,IAAI,KAAK,IAAI,KAAK,YAAY,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,CAAC;CAIhE,MAAM,2BAAW,IAAI,IAAY;CACjC,MAAM,0BAAU,IAAI,IAAY;CAChC,MAAM,gBAAgB,SAA0B;EAC9C,IAAI,SAAS,IAAI,IAAI,GAAG,OAAO;EAC/B,IAAI,QAAQ,IAAI,IAAI,GAAG,OAAO;EAC9B,SAAS,IAAI,IAAI;EACjB,KAAK,MAAM,QAAQ,MAAM,IAAI,IAAI,KAAK,CAAC,GACrC,IAAI,aAAa,IAAI,GAAG,OAAO;EAEjC,SAAS,OAAO,IAAI;EACpB,QAAQ,IAAI,IAAI;EAChB,OAAO;CACT;CAEA,KAAK,MAAM,QAAQ,MAAM,KAAK,GAC5B,IAAI,aAAa,IAAI,GAAG,OAAO;CAEjC,OAAO;AACT;;AAGA,SAAgB,aAAa,UAAkD;CAC7E,MAAM,yBAAS,IAAI,IAAsB;CACzC,KAAK,MAAM,QAAQ,UACjB,KAAK,MAAM,OAAO,KAAK,aAAa,CAAC,GAAG;EACtC,MAAM,aAAa,OAAO,IAAI,GAAG,KAAK,CAAC;EACvC,WAAW,KAAK,KAAK,EAAE;EACvB,OAAO,IAAI,KAAK,UAAU;CAC5B;CAEF,OAAO;AACT;;;;;;;;AASA,SAAgB,mBAAmB,UAA2B,MAAsB;CAClF,QAAQ,KAAK,aAAa,CAAC,EAAA,CAAG,QAAQ,QAAQ;EAC5C,MAAM,UAAU,SAAS,MAAM,cAAc,UAAU,OAAO,GAAG;EACjE,IAAI,CAAC,SAAS,OAAO;EACrB,OAAO,QAAQ,WAAW,oBAAoB,QAAQ,WAAW;CACnE,CAAC;AACH;AAEA,SAAgB,UAAU,UAA2B,MAAqB;CACxE,OAAO,mBAAmB,UAAU,IAAI,CAAC,CAAC,SAAS;AACrD"}
@@ -0,0 +1,92 @@
1
+ //#region src/models/taskGraph.ts
2
+ const COMPLETED_STATUS = "completed";
3
+ const DELETED_STATUS = "deleted";
4
+ /** True when a non-empty list has no visible work left to finish. */
5
+ function isTaskListComplete(taskList) {
6
+ const visible = taskList.filter((task) => task.status !== DELETED_STATUS);
7
+ return visible.length > 0 && visible.every((task) => task.status === COMPLETED_STATUS);
8
+ }
9
+ /**
10
+ * Would merging `newBlockedBy` into `taskId`'s dependencies introduce a cycle?
11
+ *
12
+ * Takes the proposed additions explicitly so the reducer can ask the question
13
+ * before mutating anything.
14
+ */
15
+ function detectCycle(taskList, taskId, newBlockedBy) {
16
+ const edges = /* @__PURE__ */ new Map();
17
+ for (const task of taskList) if (task.id === taskId) edges.set(task.id, [.../* @__PURE__ */ new Set([...task.blockedBy ?? [], ...newBlockedBy])]);
18
+ else edges.set(task.id, task.blockedBy ? [...task.blockedBy] : []);
19
+ const visiting = /* @__PURE__ */ new Set();
20
+ const visited = /* @__PURE__ */ new Set();
21
+ const hasCycleFrom = (node) => {
22
+ if (visiting.has(node)) return true;
23
+ if (visited.has(node)) return false;
24
+ visiting.add(node);
25
+ for (const next of edges.get(node) ?? []) if (hasCycleFrom(next)) return true;
26
+ visiting.delete(node);
27
+ visited.add(node);
28
+ return false;
29
+ };
30
+ for (const node of edges.keys()) if (hasCycleFrom(node)) return true;
31
+ return false;
32
+ }
33
+ /** Inverse adjacency: for each task, which tasks list it in their blockedBy. */
34
+ function deriveBlocks(taskList) {
35
+ const blocks = /* @__PURE__ */ new Map();
36
+ for (const task of taskList) for (const dep of task.blockedBy ?? []) {
37
+ const dependents = blocks.get(dep) ?? [];
38
+ dependents.push(task.id);
39
+ blocks.set(dep, dependents);
40
+ }
41
+ return blocks;
42
+ }
43
+ /**
44
+ * Blocking dependencies of a task that are not yet resolved.
45
+ *
46
+ * `completed` clears a dependency; `failed` deliberately does not, so a failed
47
+ * delegation keeps its dependents blocked instead of silently releasing work
48
+ * that was never actually finished.
49
+ */
50
+ function unresolvedBlockers(taskList, task) {
51
+ return (task.blockedBy ?? []).filter((dep) => {
52
+ const blocker = taskList.find((candidate) => candidate.id === dep);
53
+ if (!blocker) return false;
54
+ return blocker.status !== COMPLETED_STATUS && blocker.status !== DELETED_STATUS;
55
+ });
56
+ }
57
+ function isBlocked(taskList, task) {
58
+ return unresolvedBlockers(taskList, task).length > 0;
59
+ }
60
+ //#endregion
61
+ Object.defineProperty(exports, "deriveBlocks", {
62
+ enumerable: true,
63
+ get: function() {
64
+ return deriveBlocks;
65
+ }
66
+ });
67
+ Object.defineProperty(exports, "detectCycle", {
68
+ enumerable: true,
69
+ get: function() {
70
+ return detectCycle;
71
+ }
72
+ });
73
+ Object.defineProperty(exports, "isBlocked", {
74
+ enumerable: true,
75
+ get: function() {
76
+ return isBlocked;
77
+ }
78
+ });
79
+ Object.defineProperty(exports, "isTaskListComplete", {
80
+ enumerable: true,
81
+ get: function() {
82
+ return isTaskListComplete;
83
+ }
84
+ });
85
+ Object.defineProperty(exports, "unresolvedBlockers", {
86
+ enumerable: true,
87
+ get: function() {
88
+ return unresolvedBlockers;
89
+ }
90
+ });
91
+
92
+ //# sourceMappingURL=taskGraph-BskRslrr.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taskGraph-BskRslrr.cjs","names":[],"sources":["../src/models/taskGraph.ts"],"sourcesContent":["import type { Task } from './task';\n\nconst COMPLETED_STATUS: Task['status'] = 'completed';\nconst DELETED_STATUS: Task['status'] = 'deleted';\n\n/** True when a non-empty list has no visible work left to finish. */\nexport function isTaskListComplete(taskList: readonly Task[]): boolean {\n const visible = taskList.filter((task) => task.status !== DELETED_STATUS);\n return visible.length > 0 && visible.every((task) => task.status === COMPLETED_STATUS);\n}\n\n/**\n * Would merging `newBlockedBy` into `taskId`'s dependencies introduce a cycle?\n *\n * Takes the proposed additions explicitly so the reducer can ask the question\n * before mutating anything.\n */\nexport function detectCycle(taskList: readonly Task[], taskId: number, newBlockedBy: readonly number[]): boolean {\n const edges = new Map<number, number[]>();\n for (const task of taskList) {\n if (task.id === taskId) {\n edges.set(task.id, [...new Set([...(task.blockedBy ?? []), ...newBlockedBy])]);\n } else {\n edges.set(task.id, task.blockedBy ? [...task.blockedBy] : []);\n }\n }\n\n const visiting = new Set<number>();\n const visited = new Set<number>();\n const hasCycleFrom = (node: number): boolean => {\n if (visiting.has(node)) return true;\n if (visited.has(node)) return false;\n visiting.add(node);\n for (const next of edges.get(node) ?? []) {\n if (hasCycleFrom(next)) return true;\n }\n visiting.delete(node);\n visited.add(node);\n return false;\n };\n\n for (const node of edges.keys()) {\n if (hasCycleFrom(node)) return true;\n }\n return false;\n}\n\n/** Inverse adjacency: for each task, which tasks list it in their blockedBy. */\nexport function deriveBlocks(taskList: readonly Task[]): Map<number, number[]> {\n const blocks = new Map<number, number[]>();\n for (const task of taskList) {\n for (const dep of task.blockedBy ?? []) {\n const dependents = blocks.get(dep) ?? [];\n dependents.push(task.id);\n blocks.set(dep, dependents);\n }\n }\n return blocks;\n}\n\n/**\n * Blocking dependencies of a task that are not yet resolved.\n *\n * `completed` clears a dependency; `failed` deliberately does not, so a failed\n * delegation keeps its dependents blocked instead of silently releasing work\n * that was never actually finished.\n */\nexport function unresolvedBlockers(taskList: readonly Task[], task: Task): number[] {\n return (task.blockedBy ?? []).filter((dep) => {\n const blocker = taskList.find((candidate) => candidate.id === dep);\n if (!blocker) return false;\n return blocker.status !== COMPLETED_STATUS && blocker.status !== DELETED_STATUS;\n });\n}\n\nexport function isBlocked(taskList: readonly Task[], task: Task): boolean {\n return unresolvedBlockers(taskList, task).length > 0;\n}\n"],"mappings":";AAEA,MAAM,mBAAmC;AACzC,MAAM,iBAAiC;;AAGvC,SAAgB,mBAAmB,UAAoC;CACrE,MAAM,UAAU,SAAS,QAAQ,SAAS,KAAK,WAAW,cAAc;CACxE,OAAO,QAAQ,SAAS,KAAK,QAAQ,OAAO,SAAS,KAAK,WAAW,gBAAgB;AACvF;;;;;;;AAQA,SAAgB,YAAY,UAA2B,QAAgB,cAA0C;CAC/G,MAAM,wBAAQ,IAAI,IAAsB;CACxC,KAAK,MAAM,QAAQ,UACjB,IAAI,KAAK,OAAO,QACd,MAAM,IAAI,KAAK,IAAI,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAI,KAAK,aAAa,CAAC,GAAI,GAAG,YAAY,CAAC,CAAC,CAAC;MAE7E,MAAM,IAAI,KAAK,IAAI,KAAK,YAAY,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC,CAAC;CAIhE,MAAM,2BAAW,IAAI,IAAY;CACjC,MAAM,0BAAU,IAAI,IAAY;CAChC,MAAM,gBAAgB,SAA0B;EAC9C,IAAI,SAAS,IAAI,IAAI,GAAG,OAAO;EAC/B,IAAI,QAAQ,IAAI,IAAI,GAAG,OAAO;EAC9B,SAAS,IAAI,IAAI;EACjB,KAAK,MAAM,QAAQ,MAAM,IAAI,IAAI,KAAK,CAAC,GACrC,IAAI,aAAa,IAAI,GAAG,OAAO;EAEjC,SAAS,OAAO,IAAI;EACpB,QAAQ,IAAI,IAAI;EAChB,OAAO;CACT;CAEA,KAAK,MAAM,QAAQ,MAAM,KAAK,GAC5B,IAAI,aAAa,IAAI,GAAG,OAAO;CAEjC,OAAO;AACT;;AAGA,SAAgB,aAAa,UAAkD;CAC7E,MAAM,yBAAS,IAAI,IAAsB;CACzC,KAAK,MAAM,QAAQ,UACjB,KAAK,MAAM,OAAO,KAAK,aAAa,CAAC,GAAG;EACtC,MAAM,aAAa,OAAO,IAAI,GAAG,KAAK,CAAC;EACvC,WAAW,KAAK,KAAK,EAAE;EACvB,OAAO,IAAI,KAAK,UAAU;CAC5B;CAEF,OAAO;AACT;;;;;;;;AASA,SAAgB,mBAAmB,UAA2B,MAAsB;CAClF,QAAQ,KAAK,aAAa,CAAC,EAAA,CAAG,QAAQ,QAAQ;EAC5C,MAAM,UAAU,SAAS,MAAM,cAAc,UAAU,OAAO,GAAG;EACjE,IAAI,CAAC,SAAS,OAAO;EACrB,OAAO,QAAQ,WAAW,oBAAoB,QAAQ,WAAW;CACnE,CAAC;AACH;AAEA,SAAgB,UAAU,UAA2B,MAAqB;CACxE,OAAO,mBAAmB,UAAU,IAAI,CAAC,CAAC,SAAS;AACrD"}
@@ -0,0 +1,25 @@
1
+ import { s as Task } from "./task-DJpTteeJ.mjs";
2
+ //#region src/models/taskGraph.d.ts
3
+ /** True when a non-empty list has no visible work left to finish. */
4
+ declare function isTaskListComplete(taskList: readonly Task[]): boolean;
5
+ /**
6
+ * Would merging `newBlockedBy` into `taskId`'s dependencies introduce a cycle?
7
+ *
8
+ * Takes the proposed additions explicitly so the reducer can ask the question
9
+ * before mutating anything.
10
+ */
11
+ declare function detectCycle(taskList: readonly Task[], taskId: number, newBlockedBy: readonly number[]): boolean;
12
+ /** Inverse adjacency: for each task, which tasks list it in their blockedBy. */
13
+ declare function deriveBlocks(taskList: readonly Task[]): Map<number, number[]>;
14
+ /**
15
+ * Blocking dependencies of a task that are not yet resolved.
16
+ *
17
+ * `completed` clears a dependency; `failed` deliberately does not, so a failed
18
+ * delegation keeps its dependents blocked instead of silently releasing work
19
+ * that was never actually finished.
20
+ */
21
+ declare function unresolvedBlockers(taskList: readonly Task[], task: Task): number[];
22
+ declare function isBlocked(taskList: readonly Task[], task: Task): boolean;
23
+ //#endregion
24
+ export { unresolvedBlockers as a, isTaskListComplete as i, detectCycle as n, isBlocked as r, deriveBlocks as t };
25
+ //# sourceMappingURL=taskGraph-D0zFHF2p.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taskGraph-D0zFHF2p.d.mts","names":[],"sources":["../src/models/taskGraph.ts"],"mappings":";;;iBAMgB,mBAAmB,mBAAmB;;;;;;;iBAWtC,YAAY,mBAAmB,QAAQ,gBAAgB;;iBA+BvD,aAAa,mBAAmB,SAAS;;;;;;;;iBAmBzC,mBAAmB,mBAAmB,QAAQ,MAAM;iBAQpD,UAAU,mBAAmB,QAAQ,MAAM"}