@openacme/tasks 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/ports.d.ts +116 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +43 -0
- package/dist/ports.js.map +1 -0
- package/dist/prompt-render.d.ts +19 -0
- package/dist/prompt-render.d.ts.map +1 -0
- package/dist/prompt-render.js +204 -0
- package/dist/prompt-render.js.map +1 -0
- package/dist/recurrence.d.ts +21 -0
- package/dist/recurrence.d.ts.map +1 -0
- package/dist/recurrence.js +117 -0
- package/dist/recurrence.js.map +1 -0
- package/dist/store.d.ts +156 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +849 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +115 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +75 -0
- package/dist/types.js.map +1 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Utkarsh Kanwat
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { TASK_STATUSES, TaskStatusSchema, TaskFrontmatterSchema, RECURRENCE_SESSION_MODES, RecurrenceSchema, MIN_INTERVAL_MS, MAX_RECURRENCE_COUNT, type Recurrence, type RecurrenceSession, type Task, type TaskCreate, type TaskFrontmatter, type TaskListFilter, type TaskStatus, type TaskUpdate, } from "./types.js";
|
|
2
|
+
export { computeNextFire, validateRecurrence, describeRecurrence, } from "./recurrence.js";
|
|
3
|
+
export { TaskStore, TaskStoreError, type OnChangeFn, type TaskStoreOptions, } from "./store.js";
|
|
4
|
+
export { COMMENT_KINDS, EVENT_KINDS, INBOX_KINDS, INBOX_SOURCES, } from "./ports.js";
|
|
5
|
+
export type { Comment, CommentInput, CommentKind, CommentListOptions, CommentStorePort, EventInput, EventKind, EventStorePort, InboxKind, InboxSource, TaskEvent, } from "./ports.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,IAAI,EACT,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,SAAS,EACT,cAAc,EACd,KAAK,UAAU,EACf,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,aAAa,EACb,WAAW,EACX,WAAW,EACX,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,OAAO,EACP,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,cAAc,EACd,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { TASK_STATUSES, TaskStatusSchema, TaskFrontmatterSchema, RECURRENCE_SESSION_MODES, RecurrenceSchema, MIN_INTERVAL_MS, MAX_RECURRENCE_COUNT, } from "./types.js";
|
|
2
|
+
export { computeNextFire, validateRecurrence, describeRecurrence, } from "./recurrence.js";
|
|
3
|
+
export { TaskStore, TaskStoreError, } from "./store.js";
|
|
4
|
+
export { COMMENT_KINDS, EVENT_KINDS, INBOX_KINDS, INBOX_SOURCES, } from "./ports.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,GASrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,SAAS,EACT,cAAc,GAGf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,aAAa,EACb,WAAW,EACX,WAAW,EACX,aAAa,GACd,MAAM,YAAY,CAAC"}
|
package/dist/ports.d.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural ports for external stores TaskStore depends on.
|
|
3
|
+
* Defined here (not imported from @openacme/db) so the tasks package
|
|
4
|
+
* stays free of a runtime dep on the DB. AgentManager wires the
|
|
5
|
+
* concrete DB-backed stores in; tests can pass mocks or omit them.
|
|
6
|
+
*
|
|
7
|
+
* **All "kind" unions for the task subsystem live here** — `CommentKind`
|
|
8
|
+
* for comments, `EventKind` for events. Magic strings elsewhere should
|
|
9
|
+
* narrow to one of these. Add new kinds at this central spot and the
|
|
10
|
+
* tools / routes / web mirrors will surface type errors at every site
|
|
11
|
+
* that needs updating.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Canonical comment kinds. `null` (or undefined) = generic, untagged.
|
|
15
|
+
* Add new kinds HERE — every other place that types kinds (DB schema's
|
|
16
|
+
* drizzle enum hint, DB store types, web mirror) imports from here so
|
|
17
|
+
* the type checker surfaces every reader site that needs an update.
|
|
18
|
+
*/
|
|
19
|
+
export declare const COMMENT_KINDS: readonly ["result", "system"];
|
|
20
|
+
export type CommentKind = (typeof COMMENT_KINDS)[number];
|
|
21
|
+
export interface Comment {
|
|
22
|
+
id: string;
|
|
23
|
+
taskId: string;
|
|
24
|
+
author: string;
|
|
25
|
+
kind: CommentKind | null;
|
|
26
|
+
body: string;
|
|
27
|
+
createdAt: number;
|
|
28
|
+
}
|
|
29
|
+
export interface CommentInput {
|
|
30
|
+
taskId: string;
|
|
31
|
+
author: string;
|
|
32
|
+
body: string;
|
|
33
|
+
kind?: CommentKind | null;
|
|
34
|
+
}
|
|
35
|
+
export interface CommentListOptions {
|
|
36
|
+
limit?: number;
|
|
37
|
+
sinceTs?: number;
|
|
38
|
+
/** Inclusive kind filter. Pass `null` in the array to also include
|
|
39
|
+
* untagged (default-kind) comments alongside the named kinds. */
|
|
40
|
+
kinds?: (CommentKind | null)[];
|
|
41
|
+
}
|
|
42
|
+
export interface CommentStorePort {
|
|
43
|
+
add(input: CommentInput): Comment;
|
|
44
|
+
list(taskId: string, opts?: CommentListOptions): Comment[];
|
|
45
|
+
latestResult(taskId: string): Comment | null;
|
|
46
|
+
countByTask(taskIds: string[]): Map<string, number>;
|
|
47
|
+
deleteByTask(taskId: string): void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Canonical event kinds (single source of truth — see COMMENT_KINDS
|
|
51
|
+
* note above). Adding a new kind: extend the list here, then a single
|
|
52
|
+
* recompile narrows every reader (DB schema, store, prompt rendering,
|
|
53
|
+
* web activity log).
|
|
54
|
+
*/
|
|
55
|
+
export declare const EVENT_KINDS: readonly ["task_assigned", "status_changed", "dep_unblocked", "comment_added", "task_deleted", "scheduler_action", "task_completed_run", "ping_user"];
|
|
56
|
+
export type EventKind = (typeof EVENT_KINDS)[number];
|
|
57
|
+
/**
|
|
58
|
+
* Inbox row kinds. `user_message` payload is a UIMessage; `system_notice`
|
|
59
|
+
* payload is a small structured note rendered as text. Add new kinds
|
|
60
|
+
* here; the renderer in agent-core branches on this enum.
|
|
61
|
+
*/
|
|
62
|
+
export declare const INBOX_KINDS: readonly ["user_message", "system_notice"];
|
|
63
|
+
export type InboxKind = (typeof INBOX_KINDS)[number];
|
|
64
|
+
export declare const INBOX_SOURCES: readonly ["user", "system"];
|
|
65
|
+
export type InboxSource = (typeof INBOX_SOURCES)[number];
|
|
66
|
+
export interface EventInput {
|
|
67
|
+
/** Task this event is anchored to. Nullable for session-level events
|
|
68
|
+
* (e.g. `ping_user` where the agent is not currently on a task).
|
|
69
|
+
* At least one of (taskId, sessionId) must be set. */
|
|
70
|
+
taskId?: string | null;
|
|
71
|
+
/** Session this event is anchored to. Auto-derived from the task's
|
|
72
|
+
* current session_id if absent and taskId is present. Required for
|
|
73
|
+
* session-level events with no task anchor. */
|
|
74
|
+
sessionId?: string | null;
|
|
75
|
+
/** Semantic owner of the event (usually the assignee). Used for the
|
|
76
|
+
* prompt's "actor X" rendering when no explicit actor is recorded. */
|
|
77
|
+
agentId: string;
|
|
78
|
+
/**
|
|
79
|
+
* Echo-suppression key, NOT a causation field. The scheduler suppresses
|
|
80
|
+
* wake when `event.actor === sessionTarget.agentId` — i.e. "this agent's
|
|
81
|
+
* own session should not wake from this event."
|
|
82
|
+
*
|
|
83
|
+
* When emitting a new event, pick the value by asking: would waking
|
|
84
|
+
* the actor's own session here cause a runaway loop (agent acts → wakes
|
|
85
|
+
* → acts again)? If yes, set `actor` to the actor's agent id. If no,
|
|
86
|
+
* use `null` — every involved session, including the actor's own,
|
|
87
|
+
* should wake.
|
|
88
|
+
*
|
|
89
|
+
* Causation and echo-target USUALLY coincide (status_changed,
|
|
90
|
+
* comment_added — the agent did the thing in their session, no need
|
|
91
|
+
* to wake themselves again). They DIVERGE for cross-session fan-out
|
|
92
|
+
* events like `dep_unblocked`: the closer caused it, but the wake
|
|
93
|
+
* target is a different session, so `actor: null` is correct
|
|
94
|
+
* (otherwise same-agent cross-session deps never wake).
|
|
95
|
+
*/
|
|
96
|
+
actor?: string | null;
|
|
97
|
+
kind: EventKind;
|
|
98
|
+
payload?: unknown;
|
|
99
|
+
}
|
|
100
|
+
export interface TaskEvent {
|
|
101
|
+
id: string;
|
|
102
|
+
taskId: string | null;
|
|
103
|
+
sessionId: string | null;
|
|
104
|
+
agentId: string;
|
|
105
|
+
/** The actor that caused this event, if recorded. Null/undefined for
|
|
106
|
+
* anonymous / system events that should never be echo-suppressed. */
|
|
107
|
+
actor?: string | null;
|
|
108
|
+
kind: string;
|
|
109
|
+
payload: string | null;
|
|
110
|
+
createdAt: number;
|
|
111
|
+
}
|
|
112
|
+
export interface EventStorePort {
|
|
113
|
+
append(input: EventInput): unknown;
|
|
114
|
+
recentForTasks(taskIds: string[], sinceTs: number, limit?: number): TaskEvent[];
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,aAAa,+BAAgC,CAAC;AAC3D,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;sEACkE;IAClE,KAAK,CAAC,EAAE,CAAC,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC;IAClC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,EAAE,CAAC;IAC3D,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAC7C,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpD,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,uJASd,CAAC;AACX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,WAAW,4CAA6C,CAAC;AACtE,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,eAAO,MAAM,aAAa,6BAA8B,CAAC;AACzD,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,MAAM,WAAW,UAAU;IACzB;;2DAEuD;IACvD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB;;oDAEgD;IAChD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;2EACuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;;;;;;;;OAiBG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB;0EACsE;IACtE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC;IACnC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;CACjF"}
|
package/dist/ports.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural ports for external stores TaskStore depends on.
|
|
3
|
+
* Defined here (not imported from @openacme/db) so the tasks package
|
|
4
|
+
* stays free of a runtime dep on the DB. AgentManager wires the
|
|
5
|
+
* concrete DB-backed stores in; tests can pass mocks or omit them.
|
|
6
|
+
*
|
|
7
|
+
* **All "kind" unions for the task subsystem live here** — `CommentKind`
|
|
8
|
+
* for comments, `EventKind` for events. Magic strings elsewhere should
|
|
9
|
+
* narrow to one of these. Add new kinds at this central spot and the
|
|
10
|
+
* tools / routes / web mirrors will surface type errors at every site
|
|
11
|
+
* that needs updating.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Canonical comment kinds. `null` (or undefined) = generic, untagged.
|
|
15
|
+
* Add new kinds HERE — every other place that types kinds (DB schema's
|
|
16
|
+
* drizzle enum hint, DB store types, web mirror) imports from here so
|
|
17
|
+
* the type checker surfaces every reader site that needs an update.
|
|
18
|
+
*/
|
|
19
|
+
export const COMMENT_KINDS = ["result", "system"];
|
|
20
|
+
/**
|
|
21
|
+
* Canonical event kinds (single source of truth — see COMMENT_KINDS
|
|
22
|
+
* note above). Adding a new kind: extend the list here, then a single
|
|
23
|
+
* recompile narrows every reader (DB schema, store, prompt rendering,
|
|
24
|
+
* web activity log).
|
|
25
|
+
*/
|
|
26
|
+
export const EVENT_KINDS = [
|
|
27
|
+
"task_assigned",
|
|
28
|
+
"status_changed",
|
|
29
|
+
"dep_unblocked",
|
|
30
|
+
"comment_added",
|
|
31
|
+
"task_deleted",
|
|
32
|
+
"scheduler_action",
|
|
33
|
+
"task_completed_run",
|
|
34
|
+
"ping_user",
|
|
35
|
+
];
|
|
36
|
+
/**
|
|
37
|
+
* Inbox row kinds. `user_message` payload is a UIMessage; `system_notice`
|
|
38
|
+
* payload is a small structured note rendered as text. Add new kinds
|
|
39
|
+
* here; the renderer in agent-core branches on this enum.
|
|
40
|
+
*/
|
|
41
|
+
export const INBOX_KINDS = ["user_message", "system_notice"];
|
|
42
|
+
export const INBOX_SOURCES = ["user", "system"];
|
|
43
|
+
//# sourceMappingURL=ports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAmC3D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,eAAe;IACf,gBAAgB;IAChB,eAAe;IACf,eAAe;IACf,cAAc;IACd,kBAAkB;IAClB,oBAAoB;IACpB,WAAW;CACH,CAAC;AAGX;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,cAAc,EAAE,eAAe,CAAU,CAAC;AAGtE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAU,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown rendering for tasks — the "Tasks" and "Recent activity"
|
|
3
|
+
* blocks in the agent's system prompt, plus event-payload summaries.
|
|
4
|
+
*
|
|
5
|
+
* Pure functions: take task data + lookups, return strings. No I/O.
|
|
6
|
+
* TaskStore wraps these as methods for callers; tests can call them
|
|
7
|
+
* directly.
|
|
8
|
+
*/
|
|
9
|
+
import type { Comment } from "./ports.js";
|
|
10
|
+
import type { Task } from "./types.js";
|
|
11
|
+
export interface PromptRenderDeps {
|
|
12
|
+
list: () => Task[];
|
|
13
|
+
commentCounts: (taskIds: string[]) => Map<string, number>;
|
|
14
|
+
latestNonSystemComment: (taskId: string) => Comment | null;
|
|
15
|
+
}
|
|
16
|
+
export declare function renderForPrompt(deps: PromptRenderDeps, agentId: string, currentSessionId: string, sessionExistsFn: (sid: string) => boolean, now?: Date): string;
|
|
17
|
+
export declare function renderRecentActivity(events: import("./ports.js").TaskEvent[], titlesById: Map<string, string>, now?: Date): string;
|
|
18
|
+
export declare function summarizeEventPayload(e: import("./ports.js").TaskEvent): string;
|
|
19
|
+
//# sourceMappingURL=prompt-render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-render.d.ts","sourceRoot":"","sources":["../src/prompt-render.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,KAAK,EAAE,IAAI,EAAmB,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC;IACnB,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1D,sBAAsB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC;CAC5D;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EACzC,GAAG,GAAE,IAAiB,GACrB,MAAM,CAyHR;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,OAAO,YAAY,EAAE,SAAS,EAAE,EACxC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,GAAG,GAAE,IAAiB,GACrB,MAAM,CAgBR;AAED,wBAAgB,qBAAqB,CACnC,CAAC,EAAE,OAAO,YAAY,EAAE,SAAS,GAChC,MAAM,CAiDR"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown rendering for tasks — the "Tasks" and "Recent activity"
|
|
3
|
+
* blocks in the agent's system prompt, plus event-payload summaries.
|
|
4
|
+
*
|
|
5
|
+
* Pure functions: take task data + lookups, return strings. No I/O.
|
|
6
|
+
* TaskStore wraps these as methods for callers; tests can call them
|
|
7
|
+
* directly.
|
|
8
|
+
*/
|
|
9
|
+
import { describeRecurrence } from "./recurrence.js";
|
|
10
|
+
export function renderForPrompt(deps, agentId, currentSessionId, sessionExistsFn, now = new Date()) {
|
|
11
|
+
const all = deps.list();
|
|
12
|
+
const byId = new Map(all.map((t) => [t.id, t]));
|
|
13
|
+
const mine = all.filter((t) => t.assignee === agentId);
|
|
14
|
+
const createdByMe = all.filter((t) => t.created_by === agentId &&
|
|
15
|
+
t.assignee !== agentId &&
|
|
16
|
+
t.status !== "done" &&
|
|
17
|
+
t.status !== "canceled");
|
|
18
|
+
const inThisSession = mine.filter((t) => t.session_id === currentSessionId);
|
|
19
|
+
const active = inThisSession.filter((t) => t.status === "in_progress");
|
|
20
|
+
const queuedHere = inThisSession
|
|
21
|
+
.filter((t) => t.status === "open" &&
|
|
22
|
+
depsSatisfied(t.depends_on, byId) &&
|
|
23
|
+
!isFutureStart(t.start_at, now))
|
|
24
|
+
.sort((a, b) => a.created_at.localeCompare(b.created_at));
|
|
25
|
+
const scheduledLater = inThisSession
|
|
26
|
+
.filter((t) => t.status === "open" && isFutureStart(t.start_at, now))
|
|
27
|
+
.sort((a, b) => (a.start_at ?? "").localeCompare(b.start_at ?? ""));
|
|
28
|
+
const blocked = inThisSession.filter((t) => t.status === "blocked");
|
|
29
|
+
const otherSessions = mine.filter((t) => {
|
|
30
|
+
if (t.session_id === currentSessionId)
|
|
31
|
+
return false;
|
|
32
|
+
if (t.status === "done" || t.status === "canceled")
|
|
33
|
+
return false;
|
|
34
|
+
if (!t.session_id)
|
|
35
|
+
return false;
|
|
36
|
+
return sessionExistsFn(t.session_id);
|
|
37
|
+
});
|
|
38
|
+
// Bulk-lookup comment counts so a queue of hundreds doesn't fan out.
|
|
39
|
+
const visibleIds = new Set();
|
|
40
|
+
for (const t of [
|
|
41
|
+
...active,
|
|
42
|
+
...queuedHere,
|
|
43
|
+
...scheduledLater,
|
|
44
|
+
...blocked,
|
|
45
|
+
...otherSessions,
|
|
46
|
+
...createdByMe,
|
|
47
|
+
]) {
|
|
48
|
+
visibleIds.add(t.id);
|
|
49
|
+
}
|
|
50
|
+
const counts = deps.commentCounts(Array.from(visibleIds));
|
|
51
|
+
const tag = (t) => {
|
|
52
|
+
const n = counts.get(t.id) ?? 0;
|
|
53
|
+
return n > 0 ? ` (${n} comment${n === 1 ? "" : "s"})` : "";
|
|
54
|
+
};
|
|
55
|
+
const sections = [];
|
|
56
|
+
if (active.length > 0) {
|
|
57
|
+
sections.push(renderSection("Active in this session (currently working)", active, (t) => {
|
|
58
|
+
const due = t.due_at ? ` (due ${t.due_at})` : "";
|
|
59
|
+
return `- [${t.id}]${due} ${t.title}${recurrenceTag(t)}${tag(t)}`;
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
if (queuedHere.length > 0) {
|
|
63
|
+
sections.push(renderSection("Queued in this session (next up, in order)", queuedHere, (t) => `- [${t.id}] ${t.title}${recurrenceTag(t)}${tag(t)}`));
|
|
64
|
+
}
|
|
65
|
+
if (scheduledLater.length > 0) {
|
|
66
|
+
sections.push(renderSection("Scheduled later (in this session, starts at T)", scheduledLater, (t) => `- [${t.id}] starts ${t.start_at} — ${t.title}${recurrenceTag(t)}${tag(t)}`));
|
|
67
|
+
}
|
|
68
|
+
if (blocked.length > 0) {
|
|
69
|
+
sections.push(renderSection("Blocked on dependencies", blocked, (t) => {
|
|
70
|
+
const unmet = t.depends_on.filter((d) => {
|
|
71
|
+
const dep = byId.get(d);
|
|
72
|
+
return !dep || dep.status !== "done";
|
|
73
|
+
});
|
|
74
|
+
return `- [${t.id}] ${t.title}${recurrenceTag(t)}${tag(t)} — waiting on [${unmet.join(", ")}]`;
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
if (otherSessions.length > 0) {
|
|
78
|
+
sections.push(renderSection("In another session (read-only awareness — don't re-handle)", otherSessions, (t) => `- [${t.id}] ${t.title}${recurrenceTag(t)}${tag(t)} — bound to session ${t.session_id}`));
|
|
79
|
+
}
|
|
80
|
+
if (createdByMe.length > 0) {
|
|
81
|
+
// Recent-activity hint for delegated tasks — the only signal that
|
|
82
|
+
// pulls assigners back when an assignee touches their work.
|
|
83
|
+
sections.push(renderSection("Created by me, assigned to others", createdByMe, (t) => {
|
|
84
|
+
const recent = deps.latestNonSystemComment(t.id);
|
|
85
|
+
const hint = recent
|
|
86
|
+
? ` — last comment by ${recent.author} ${formatRelativeFrom(recent.createdAt, now)}${recent.kind ? ` (${recent.kind})` : ""}`
|
|
87
|
+
: "";
|
|
88
|
+
return `- [${t.id}] ${t.title}${recurrenceTag(t)}${tag(t)} — assignee ${t.assignee}, status ${t.status}${hint}`;
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
if (sections.length === 0)
|
|
92
|
+
return "";
|
|
93
|
+
return `${sections.join("\n\n")}\n\nNOTE: snapshot from session start. Call task_list for fresh state.`;
|
|
94
|
+
}
|
|
95
|
+
export function renderRecentActivity(events, titlesById, now = new Date()) {
|
|
96
|
+
if (events.length === 0)
|
|
97
|
+
return "";
|
|
98
|
+
const lines = events.map((e) => {
|
|
99
|
+
const when = formatRelativeFrom(e.createdAt, now);
|
|
100
|
+
const summary = summarizeEventPayload(e);
|
|
101
|
+
const tail = summary ? ` — ${summary}` : "";
|
|
102
|
+
if (!e.taskId) {
|
|
103
|
+
// Session-level event with no task anchor. Format without the
|
|
104
|
+
// bracketed task id; the kind + payload summary already carries
|
|
105
|
+
// the context the agent needs.
|
|
106
|
+
return `- ${when} · ${e.kind}${tail}`;
|
|
107
|
+
}
|
|
108
|
+
const title = titlesById.get(e.taskId) ?? "(unknown task)";
|
|
109
|
+
return `- ${when} · ${e.kind} on [${e.taskId}] ${title}${tail}`;
|
|
110
|
+
});
|
|
111
|
+
return lines.join("\n");
|
|
112
|
+
}
|
|
113
|
+
export function summarizeEventPayload(e) {
|
|
114
|
+
if (!e.payload)
|
|
115
|
+
return `actor ${e.agentId}`;
|
|
116
|
+
let p = {};
|
|
117
|
+
try {
|
|
118
|
+
p = JSON.parse(e.payload);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return `actor ${e.agentId}`;
|
|
122
|
+
}
|
|
123
|
+
switch (e.kind) {
|
|
124
|
+
case "comment_added": {
|
|
125
|
+
// `e.agentId` is the recipient (task assignee). The author is in
|
|
126
|
+
// `payload.author` (or `e.actor` for non-system authors). Fall
|
|
127
|
+
// back to `e.actor` for older events that pre-date the payload
|
|
128
|
+
// change; treat null actor as "system".
|
|
129
|
+
const author = (p.author && String(p.author)) || e.actor || "system";
|
|
130
|
+
const lead = p.kind
|
|
131
|
+
? `${String(p.kind)} comment by ${author}`
|
|
132
|
+
: `comment by ${author}`;
|
|
133
|
+
const excerpt = p.excerpt ? String(p.excerpt) : "";
|
|
134
|
+
return excerpt ? `${lead}: "${excerpt}"` : lead;
|
|
135
|
+
}
|
|
136
|
+
case "status_changed":
|
|
137
|
+
return `${String(p.from ?? "?")} → ${String(p.to ?? "?")}`;
|
|
138
|
+
case "dep_unblocked":
|
|
139
|
+
return `dep ${String(p.blocked_by_task_id ?? "?")} done — now runnable`;
|
|
140
|
+
case "task_assigned":
|
|
141
|
+
return `assigned to ${String(p.assignee ?? "?")} by ${String(p.created_by ?? "?")}`;
|
|
142
|
+
case "task_deleted":
|
|
143
|
+
return p.forced ? `deleted (cascaded)` : `deleted`;
|
|
144
|
+
case "scheduler_action": {
|
|
145
|
+
const action = String(p.action ?? "?");
|
|
146
|
+
const message = String(p.message ?? "");
|
|
147
|
+
return message ? `[${action}] ${message}` : `[${action}]`;
|
|
148
|
+
}
|
|
149
|
+
case "task_completed_run": {
|
|
150
|
+
const runs = p.runs != null ? String(p.runs) : "?";
|
|
151
|
+
const next = p.next_fire ? ` — next fire ${String(p.next_fire)}` : "";
|
|
152
|
+
return `run ${runs} done${next}`;
|
|
153
|
+
}
|
|
154
|
+
case "ping_user": {
|
|
155
|
+
const excerpt = p.message ? String(p.message).slice(0, 80) : "";
|
|
156
|
+
return excerpt
|
|
157
|
+
? `${e.agentId} pinged user: "${excerpt}"`
|
|
158
|
+
: `${e.agentId} pinged user`;
|
|
159
|
+
}
|
|
160
|
+
default:
|
|
161
|
+
return `actor ${e.agentId}`;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// ── Internals ───────────────────────────────────────────────────────
|
|
165
|
+
function renderSection(title, items, fmt) {
|
|
166
|
+
return `${title}:\n${items.map(fmt).join("\n")}`;
|
|
167
|
+
}
|
|
168
|
+
function recurrenceTag(t) {
|
|
169
|
+
if (!t.recurrence)
|
|
170
|
+
return "";
|
|
171
|
+
return ` (${describeRecurrence(t.recurrence)}, ran ${t.runs}×)`;
|
|
172
|
+
}
|
|
173
|
+
function formatRelativeFrom(unixSeconds, now) {
|
|
174
|
+
const diffSec = Math.max(0, Math.floor(now.getTime() / 1000 - unixSeconds));
|
|
175
|
+
if (diffSec < 60)
|
|
176
|
+
return `${diffSec}s ago`;
|
|
177
|
+
const m = Math.floor(diffSec / 60);
|
|
178
|
+
if (m < 60)
|
|
179
|
+
return `${m}m ago`;
|
|
180
|
+
const h = Math.floor(m / 60);
|
|
181
|
+
if (h < 24)
|
|
182
|
+
return `${h}h ago`;
|
|
183
|
+
const d = Math.floor(h / 24);
|
|
184
|
+
return `${d}d ago`;
|
|
185
|
+
}
|
|
186
|
+
function isFutureStart(startAt, now) {
|
|
187
|
+
if (!startAt)
|
|
188
|
+
return false;
|
|
189
|
+
const t = Date.parse(startAt);
|
|
190
|
+
if (!Number.isFinite(t))
|
|
191
|
+
return false;
|
|
192
|
+
return t > now.getTime();
|
|
193
|
+
}
|
|
194
|
+
function depsSatisfied(deps, byId) {
|
|
195
|
+
for (const d of deps) {
|
|
196
|
+
const dep = byId.get(d);
|
|
197
|
+
if (!dep)
|
|
198
|
+
return false;
|
|
199
|
+
if (dep.status !== "done")
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=prompt-render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-render.js","sourceRoot":"","sources":["../src/prompt-render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Recurrence } from "./types.js";
|
|
2
|
+
export type ValidateResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
} | {
|
|
5
|
+
ok: false;
|
|
6
|
+
message: string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Compute the next fire time for a recurrence, or null if the schedule
|
|
10
|
+
* is exhausted (count cap, until cap, or cron expression that yields no
|
|
11
|
+
* future runs). Pure — does not start a Croner timer.
|
|
12
|
+
*/
|
|
13
|
+
export declare function computeNextFire(rec: Recurrence, from: Date, runs: number): Date | null;
|
|
14
|
+
/**
|
|
15
|
+
* Validate a recurrence at the write boundary. Cheaper than waiting for
|
|
16
|
+
* Croner to throw when the daemon eventually arms a malformed expr.
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateRecurrence(rec: Recurrence, now: Date): ValidateResult;
|
|
19
|
+
/** One-line cadence label for prompt rendering. */
|
|
20
|
+
export declare function describeRecurrence(rec: Recurrence): string;
|
|
21
|
+
//# sourceMappingURL=recurrence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recurrence.d.ts","sourceRoot":"","sources":["../src/recurrence.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,cAAc,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,UAAU,EACf,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,MAAM,GACX,IAAI,GAAG,IAAI,CA4Bb;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,UAAU,EACf,GAAG,EAAE,IAAI,GACR,cAAc,CAoDhB;AAED,mDAAmD;AACnD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAK1D"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Cron } from "croner";
|
|
2
|
+
import { MIN_INTERVAL_MS, MAX_RECURRENCE_COUNT, } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Compute the next fire time for a recurrence, or null if the schedule
|
|
5
|
+
* is exhausted (count cap, until cap, or cron expression that yields no
|
|
6
|
+
* future runs). Pure — does not start a Croner timer.
|
|
7
|
+
*/
|
|
8
|
+
export function computeNextFire(rec, from, runs) {
|
|
9
|
+
if (rec.until) {
|
|
10
|
+
const u = Date.parse(rec.until);
|
|
11
|
+
if (Number.isFinite(u) && u <= from.getTime())
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
if (rec.count != null && runs >= rec.count)
|
|
15
|
+
return null;
|
|
16
|
+
let candidate;
|
|
17
|
+
if (rec.kind === "cron") {
|
|
18
|
+
try {
|
|
19
|
+
const cron = new Cron(rec.expr, {
|
|
20
|
+
timezone: rec.tz ?? undefined,
|
|
21
|
+
paused: true,
|
|
22
|
+
});
|
|
23
|
+
candidate = cron.nextRun(from);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
candidate = new Date(from.getTime() + rec.every_ms);
|
|
31
|
+
}
|
|
32
|
+
if (!candidate)
|
|
33
|
+
return null;
|
|
34
|
+
if (rec.until) {
|
|
35
|
+
const u = Date.parse(rec.until);
|
|
36
|
+
if (Number.isFinite(u) && candidate.getTime() > u)
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return candidate;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validate a recurrence at the write boundary. Cheaper than waiting for
|
|
43
|
+
* Croner to throw when the daemon eventually arms a malformed expr.
|
|
44
|
+
*/
|
|
45
|
+
export function validateRecurrence(rec, now) {
|
|
46
|
+
if (rec.until) {
|
|
47
|
+
const u = Date.parse(rec.until);
|
|
48
|
+
if (!Number.isFinite(u)) {
|
|
49
|
+
return { ok: false, message: `recurrence.until is not a valid ISO date` };
|
|
50
|
+
}
|
|
51
|
+
if (u <= now.getTime()) {
|
|
52
|
+
return { ok: false, message: `recurrence.until is in the past` };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (rec.count != null) {
|
|
56
|
+
if (!Number.isInteger(rec.count) || rec.count <= 0) {
|
|
57
|
+
return { ok: false, message: `recurrence.count must be a positive integer` };
|
|
58
|
+
}
|
|
59
|
+
if (rec.count > MAX_RECURRENCE_COUNT) {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
message: `recurrence.count exceeds max (${MAX_RECURRENCE_COUNT})`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (rec.kind === "cron") {
|
|
67
|
+
try {
|
|
68
|
+
const cron = new Cron(rec.expr, {
|
|
69
|
+
timezone: rec.tz ?? undefined,
|
|
70
|
+
paused: true,
|
|
71
|
+
});
|
|
72
|
+
const next = cron.nextRun(now);
|
|
73
|
+
if (!next) {
|
|
74
|
+
return {
|
|
75
|
+
ok: false,
|
|
76
|
+
message: `recurrence.expr ${JSON.stringify(rec.expr)} has no future runs`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
message: `recurrence.expr invalid: ${e instanceof Error ? e.message : String(e)}`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return { ok: true };
|
|
87
|
+
}
|
|
88
|
+
// interval
|
|
89
|
+
if (!Number.isInteger(rec.every_ms) || rec.every_ms < MIN_INTERVAL_MS) {
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
message: `recurrence.every_ms must be an integer >= ${MIN_INTERVAL_MS}`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return { ok: true };
|
|
96
|
+
}
|
|
97
|
+
/** One-line cadence label for prompt rendering. */
|
|
98
|
+
export function describeRecurrence(rec) {
|
|
99
|
+
if (rec.kind === "cron") {
|
|
100
|
+
return rec.tz ? `cron ${rec.expr} (${rec.tz})` : `cron ${rec.expr}`;
|
|
101
|
+
}
|
|
102
|
+
return `every ${formatMs(rec.every_ms)}`;
|
|
103
|
+
}
|
|
104
|
+
function formatMs(ms) {
|
|
105
|
+
const s = Math.round(ms / 1000);
|
|
106
|
+
if (s < 60)
|
|
107
|
+
return `${s}s`;
|
|
108
|
+
const m = Math.round(s / 60);
|
|
109
|
+
if (m < 60)
|
|
110
|
+
return `${m}m`;
|
|
111
|
+
const h = Math.round(m / 60);
|
|
112
|
+
if (h < 24)
|
|
113
|
+
return `${h}h`;
|
|
114
|
+
const d = Math.round(h / 24);
|
|
115
|
+
return `${d}d`;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=recurrence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recurrence.js","sourceRoot":"","sources":["../src/recurrence.ts"],"names":[],"mappings":"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"}
|