@timqi/pier 0.1.3 → 0.1.5
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/README.md +12 -7
- package/dist/agent/config.js +9 -3
- package/dist/agent/events.js +13 -5
- package/dist/agent/listing.js +10 -2
- package/dist/agent/packages.js +4 -20
- package/dist/agent/pi.js +18 -86
- package/dist/channels/attach.js +2 -2
- package/dist/channels/commands.js +10 -8
- package/dist/channels/config.js +32 -24
- package/dist/channels/control.js +60 -14
- package/dist/channels/conversations.js +60 -13
- package/dist/channels/handoff.js +90 -0
- package/dist/channels/lark-api.js +7 -0
- package/dist/channels/lark-outbound.js +19 -0
- package/dist/channels/lark-panel.js +41 -26
- package/dist/channels/lark-render.js +2 -1
- package/dist/channels/lark.js +35 -26
- package/dist/channels/lines.js +1 -1
- package/dist/channels/panel.js +324 -115
- package/dist/channels/receipts.js +25 -12
- package/dist/channels/routes.js +21 -7
- package/dist/channels/runtime.js +21 -7
- package/dist/channels/slack-api.js +19 -10
- package/dist/channels/slack-cli.js +503 -0
- package/dist/channels/slack-directory.js +2 -2
- package/dist/channels/slack-outbound.js +11 -1
- package/dist/channels/slack-panel.js +81 -40
- package/dist/channels/slack-render.js +3 -1
- package/dist/channels/slack-thread.js +41 -0
- package/dist/channels/slack-transcript.js +107 -0
- package/dist/channels/slack.js +38 -31
- package/dist/channels/types.js +1 -3
- package/dist/cli.js +166 -7
- package/dist/core/identity.js +61 -17
- package/dist/core/reply.js +19 -15
- package/dist/core/router.js +23 -3
- package/dist/db.js +33 -0
- package/dist/main.js +61 -41
- package/dist/paths.js +3 -0
- package/dist/secrets.js +2 -1
- package/dist/settings.js +3 -12
- package/dist/socket.js +99 -0
- package/dist/tasks/agent.js +5 -6
- package/dist/tasks/callbacks.js +2 -2
- package/dist/tasks/cli.js +225 -0
- package/dist/tasks/definitions.js +3 -3
- package/dist/tasks/execution.js +1 -3
- package/dist/tasks/groups.js +2 -5
- package/dist/tasks/messages.js +18 -158
- package/dist/tasks/operations.js +335 -0
- package/dist/tasks/routes.js +1 -11
- package/dist/tasks/runs.js +5 -20
- package/dist/tasks/service.js +18 -43
- package/dist/tasks/store.js +15 -15
- package/dist/tools.js +18 -3
- package/dist/vault.js +107 -0
- package/dist/web/auth.js +2 -1
- package/dist/web/public/assets/{activity-BSMeRcN2.js → activity-CrybM-E8.js} +2 -2
- package/dist/web/public/assets/activity-CrybM-E8.js.br +0 -0
- package/dist/web/public/assets/activity-CrybM-E8.js.gz +0 -0
- package/dist/web/public/assets/{boards-BCWQMZry.js → boards-Cw7_6J6L.js} +1 -1
- package/dist/web/public/assets/boards-Cw7_6J6L.js.br +0 -0
- package/dist/web/public/assets/boards-Cw7_6J6L.js.gz +0 -0
- package/dist/web/public/assets/{explorer-Cr4XTi4j.js → explorer-DV066dUD.js} +1 -1
- package/dist/web/public/assets/explorer-DV066dUD.js.br +0 -0
- package/dist/web/public/assets/explorer-DV066dUD.js.gz +0 -0
- package/dist/web/public/assets/index-BrNHu2qj.js +85 -0
- package/dist/web/public/assets/index-BrNHu2qj.js.br +0 -0
- package/dist/web/public/assets/index-BrNHu2qj.js.gz +0 -0
- package/dist/web/public/assets/index-DLszkDUV.css +2 -0
- package/dist/web/public/assets/index-DLszkDUV.css.br +0 -0
- package/dist/web/public/assets/index-DLszkDUV.css.gz +0 -0
- package/dist/web/public/assets/{runs-DdERzeac.js → runs-DDagTNaM.js} +1 -1
- package/dist/web/public/assets/runs-DDagTNaM.js.br +0 -0
- package/dist/web/public/assets/runs-DDagTNaM.js.gz +0 -0
- package/dist/web/public/assets/settings-BEdSeXpm.js +5 -0
- package/dist/web/public/assets/settings-BEdSeXpm.js.br +0 -0
- package/dist/web/public/assets/settings-BEdSeXpm.js.gz +0 -0
- package/dist/web/public/assets/{task-runs-C-dGUDsH.js → task-runs-BnMack9t.js} +1 -1
- package/dist/web/public/assets/task-runs-BnMack9t.js.br +0 -0
- package/dist/web/public/assets/task-runs-BnMack9t.js.gz +0 -0
- package/dist/web/public/assets/{tasks-CY30H1u1.js → tasks-BkW7YShZ.js} +1 -1
- package/dist/web/public/assets/tasks-BkW7YShZ.js.br +0 -0
- package/dist/web/public/assets/tasks-BkW7YShZ.js.gz +0 -0
- package/dist/web/public/index.html +2 -2
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/push.js +4 -10
- package/dist/web/vault.js +68 -0
- package/dist/{extensions/web → websearch}/artifacts.js +2 -2
- package/dist/websearch/cli.js +73 -0
- package/dist/websearch/run.js +273 -0
- package/docs/deploy.md +34 -11
- package/package.json +2 -3
- package/skills/pier-help/SKILL.md +32 -16
- package/skills/pier-slack/SKILL.md +52 -71
- package/skills/pier-tasks/SKILL.md +60 -148
- package/skills/pier-vault/SKILL.md +36 -0
- package/skills/pier-web/SKILL.md +50 -0
- package/dist/channels/slack-tool.js +0 -416
- package/dist/channels/telegram-api.js +0 -86
- package/dist/channels/telegram-panel.js +0 -97
- package/dist/channels/telegram-render.js +0 -69
- package/dist/channels/telegram.js +0 -421
- package/dist/extensions/index.js +0 -10
- package/dist/extensions/web/index.js +0 -9
- package/dist/extensions/web/tools.js +0 -265
- package/dist/tasks/tool.js +0 -416
- package/dist/web/public/assets/activity-BSMeRcN2.js.br +0 -0
- package/dist/web/public/assets/activity-BSMeRcN2.js.gz +0 -0
- package/dist/web/public/assets/boards-BCWQMZry.js.br +0 -0
- package/dist/web/public/assets/boards-BCWQMZry.js.gz +0 -0
- package/dist/web/public/assets/explorer-Cr4XTi4j.js.br +0 -0
- package/dist/web/public/assets/explorer-Cr4XTi4j.js.gz +0 -0
- package/dist/web/public/assets/index-C7tA0Ufu.js +0 -85
- package/dist/web/public/assets/index-C7tA0Ufu.js.br +0 -0
- package/dist/web/public/assets/index-C7tA0Ufu.js.gz +0 -0
- package/dist/web/public/assets/index-DVIt5Gio.css +0 -2
- package/dist/web/public/assets/index-DVIt5Gio.css.br +0 -0
- package/dist/web/public/assets/index-DVIt5Gio.css.gz +0 -0
- package/dist/web/public/assets/runs-DdERzeac.js.br +0 -0
- package/dist/web/public/assets/runs-DdERzeac.js.gz +0 -0
- package/dist/web/public/assets/settings-CQDAHoMM.js +0 -5
- package/dist/web/public/assets/settings-CQDAHoMM.js.br +0 -0
- package/dist/web/public/assets/settings-CQDAHoMM.js.gz +0 -0
- package/dist/web/public/assets/task-runs-C-dGUDsH.js.br +0 -0
- package/dist/web/public/assets/task-runs-C-dGUDsH.js.gz +0 -0
- package/dist/web/public/assets/tasks-CY30H1u1.js.br +0 -0
- package/dist/web/public/assets/tasks-CY30H1u1.js.gz +0 -0
- /package/dist/{extensions/web → websearch}/anthropic.js +0 -0
- /package/dist/{extensions/web → websearch}/content.js +0 -0
- /package/dist/{extensions/web → websearch}/http.js +0 -0
- /package/dist/{extensions/web → websearch}/json.js +0 -0
- /package/dist/{extensions/web → websearch}/language.js +0 -0
- /package/dist/{extensions/web → websearch}/openai.js +0 -0
- /package/dist/{extensions/web → websearch}/provider.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// What a task *is* before it ever runs: the id it is minted with, the draft
|
|
2
2
|
// validated into a definition, and when its trigger is next due. Every way a
|
|
3
|
-
// definition can be created — HTTP,
|
|
3
|
+
// definition can be created — HTTP, `pier task`, Pier's own owned task —
|
|
4
4
|
// arrives here, so a field is checked in one place or nowhere.
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
6
|
import { stat } from "node:fs/promises";
|
|
@@ -24,7 +24,7 @@ export const idSymbol = (byte) => ID_ALPHABET.charAt(byte & 31);
|
|
|
24
24
|
* not an error — `saveRun`'s ON CONFLICT DO UPDATE would quietly overwrite. */
|
|
25
25
|
export const newId = () => Array.from(randomBytes(16), idSymbol).join("");
|
|
26
26
|
/** A definition Pier's own code created is reconciled by that code and edited
|
|
27
|
-
* by nobody: `creator` is `"http"` (Console) or `session:<id>` (task
|
|
27
|
+
* by nobody: `creator` is `"http"` (Console) or `session:<id>` (`pier task`);
|
|
28
28
|
* anything else is an instance-layer owner, which names itself in `by`.
|
|
29
29
|
* Otherwise a public surface could repoint the tools task while its switch
|
|
30
30
|
* went on claiming Pier keeps the tools current. */
|
|
@@ -258,7 +258,7 @@ export class TaskDefinitions {
|
|
|
258
258
|
if (trigger.type === "watch")
|
|
259
259
|
await this.assertDirectory(trigger.cwd);
|
|
260
260
|
const actionRaw = record(value.action);
|
|
261
|
-
// Self-documenting:
|
|
261
|
+
// Self-documenting: a model recovers from this in one retry.
|
|
262
262
|
if (!actionRaw) {
|
|
263
263
|
throw new Error('action required, e.g. {"type":"agent","session":{"mode":"fresh","cwd":"/abs/path"},"prompt":"..."}');
|
|
264
264
|
}
|
package/dist/tasks/execution.js
CHANGED
|
@@ -92,9 +92,7 @@ export class TaskExecution {
|
|
|
92
92
|
log.debug(`${settled} (watch did not match)`);
|
|
93
93
|
else
|
|
94
94
|
log.info(settled);
|
|
95
|
-
|
|
96
|
-
// completion callback: the pending question is the notification.
|
|
97
|
-
if (run.callbackSessionId && !this.host.openDecisionId(run.id))
|
|
95
|
+
if (run.callbackSessionId)
|
|
98
96
|
run.callbackState = "pending";
|
|
99
97
|
this.controllers.delete(run.id);
|
|
100
98
|
try {
|
package/dist/tasks/groups.js
CHANGED
|
@@ -35,12 +35,12 @@ export class TaskGroups {
|
|
|
35
35
|
}
|
|
36
36
|
/** Enqueues every member or none: a partially started group is worse than
|
|
37
37
|
* a rejected one. */
|
|
38
|
-
runAll(definitions, join, callerSessionId,
|
|
38
|
+
runAll(definitions, join, callerSessionId, callbackSessionId, callbackMode) {
|
|
39
39
|
if (definitions.length < 2)
|
|
40
40
|
throw new Error("a task group needs at least 2 members");
|
|
41
41
|
const { group, runs } = this.store.transact(() => {
|
|
42
42
|
const group = this.create(join, callerSessionId, callbackSessionId, callbackMode);
|
|
43
|
-
const runs = definitions.map((definition) => this.host.prepareMember(definition.id, group.id, callerSessionId
|
|
43
|
+
const runs = definitions.map((definition) => this.host.prepareMember(definition.id, group.id, callerSessionId));
|
|
44
44
|
group.memberRunIds = runs.map((run) => run.id);
|
|
45
45
|
this.store.saveGroup(group);
|
|
46
46
|
return { group, runs };
|
|
@@ -150,9 +150,6 @@ export class TaskGroups {
|
|
|
150
150
|
`- "${run.context.definition.name}" \u2014 state: ${run.state}`,
|
|
151
151
|
` ${runRef(run)}`,
|
|
152
152
|
];
|
|
153
|
-
const decision = this.host.openDecisionId(run.id);
|
|
154
|
-
if (decision)
|
|
155
|
-
head.push(` Needs a decision: reply to message ${decision}`);
|
|
156
153
|
if (group.join === "first" && run.id !== group.winnerRunId) {
|
|
157
154
|
head.push(" Cancelled after the winning run; resume its session to recover partial work.");
|
|
158
155
|
return head.join("\n");
|
package/dist/tasks/messages.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
// What a parent
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// What a parent says to a child while a run is going. Every message is a
|
|
2
|
+
// durable row before it is a delivery: the child may be mid-turn or gone, and
|
|
3
|
+
// an undelivered message is retried, expired and said, never dropped (§5).
|
|
4
|
+
// Delivery itself belongs to outbox.ts.
|
|
5
5
|
import { runSource } from "./callbacks.js";
|
|
6
6
|
import { newId } from "./definitions.js";
|
|
7
7
|
import { Outbox } from "./outbox.js";
|
|
8
8
|
import { isTerminal } from "./types.js";
|
|
9
9
|
const MAX_MESSAGE_LENGTH = 16 * 1024;
|
|
10
|
-
/** `
|
|
10
|
+
/** `expired` is a delivery given up on. */
|
|
11
11
|
const ENGINE_STATE = {
|
|
12
|
-
pending: "pending", failed: "failed", delivered: "delivered",
|
|
12
|
+
pending: "pending", failed: "failed", delivered: "delivered", expired: "abandoned",
|
|
13
13
|
};
|
|
14
14
|
function bounded(content) {
|
|
15
15
|
const text = content.trim();
|
|
@@ -22,20 +22,14 @@ function bounded(content) {
|
|
|
22
22
|
export class TaskMessenger {
|
|
23
23
|
store;
|
|
24
24
|
hub;
|
|
25
|
-
prepareResume;
|
|
26
25
|
unreachable;
|
|
27
|
-
startRun;
|
|
28
26
|
outbox;
|
|
29
27
|
constructor(store, router, hub,
|
|
30
|
-
/** Prepares a continuation; it starts only after the reply commits. */
|
|
31
|
-
prepareResume,
|
|
32
28
|
/** Reports a delivery nobody can complete (service.ts owns the surfaces). */
|
|
33
|
-
unreachable
|
|
29
|
+
unreachable) {
|
|
34
30
|
this.store = store;
|
|
35
31
|
this.hub = hub;
|
|
36
|
-
this.prepareResume = prepareResume;
|
|
37
32
|
this.unreachable = unreachable;
|
|
38
|
-
this.startRun = startRun;
|
|
39
33
|
this.outbox = new Outbox(router, {
|
|
40
34
|
id: ({ message }) => message.id,
|
|
41
35
|
reload: (id) => {
|
|
@@ -50,10 +44,8 @@ export class TaskMessenger {
|
|
|
50
44
|
message.state = "delivered";
|
|
51
45
|
message.deliveredAt = Date.now();
|
|
52
46
|
}
|
|
53
|
-
else if (callbackState === "abandoned")
|
|
47
|
+
else if (callbackState === "abandoned")
|
|
54
48
|
message.state = "expired";
|
|
55
|
-
message.answeredAt = Date.now();
|
|
56
|
-
}
|
|
57
49
|
else
|
|
58
50
|
message.state = callbackState ?? "pending";
|
|
59
51
|
this.store.saveMessage(message);
|
|
@@ -76,25 +68,6 @@ export class TaskMessenger {
|
|
|
76
68
|
for (const message of this.store.expirePendingMessages())
|
|
77
69
|
this.changed(message);
|
|
78
70
|
}
|
|
79
|
-
/** The unanswered decision on a run, if any. */
|
|
80
|
-
openDecisionId(runId) {
|
|
81
|
-
return this.store.listMessages(runId).find((m) => m.kind === "decision" && (m.state === "pending" || m.state === "delivered"))?.id ?? null;
|
|
82
|
-
}
|
|
83
|
-
/** A manual continuation supersedes an unanswered decision (design 04):
|
|
84
|
-
* one continuation per run, never two racing ones. */
|
|
85
|
-
expireDecisions(runId, reason) {
|
|
86
|
-
const expired = [];
|
|
87
|
-
for (const message of this.store.listMessages(runId)) {
|
|
88
|
-
if (message.kind !== "decision" || (message.state !== "pending" && message.state !== "delivered"))
|
|
89
|
-
continue;
|
|
90
|
-
message.state = "expired";
|
|
91
|
-
message.error = reason;
|
|
92
|
-
message.answeredAt = Date.now();
|
|
93
|
-
this.store.saveMessage(message);
|
|
94
|
-
expired.push(message);
|
|
95
|
-
}
|
|
96
|
-
return expired;
|
|
97
|
-
}
|
|
98
71
|
list(runId) {
|
|
99
72
|
return this.store.listMessages(runId);
|
|
100
73
|
}
|
|
@@ -102,7 +75,7 @@ export class TaskMessenger {
|
|
|
102
75
|
return this.store.listRecentMessages(since);
|
|
103
76
|
}
|
|
104
77
|
async control(run, fromSessionId, kind, content) {
|
|
105
|
-
const message = this.create(run, kind, fromSessionId, run.targetSessionId ?? "", content
|
|
78
|
+
const message = this.create(run, kind, fromSessionId, run.targetSessionId ?? "", content);
|
|
106
79
|
this.changed(message);
|
|
107
80
|
if (run.targetSessionId)
|
|
108
81
|
this.deliver(message, run.targetSessionId);
|
|
@@ -112,9 +85,8 @@ export class TaskMessenger {
|
|
|
112
85
|
if (!run.targetSessionId)
|
|
113
86
|
return;
|
|
114
87
|
for (const message of this.store.listMessages(run.id)) {
|
|
115
|
-
if (message.state
|
|
116
|
-
|
|
117
|
-
this.deliver(message, run.targetSessionId);
|
|
88
|
+
if (message.state === "pending")
|
|
89
|
+
this.deliver(message, run.targetSessionId);
|
|
118
90
|
}
|
|
119
91
|
}
|
|
120
92
|
/** Delivery is fire-and-forget, so this sweep is what closes a failed one.
|
|
@@ -124,30 +96,13 @@ export class TaskMessenger {
|
|
|
124
96
|
if (!run)
|
|
125
97
|
continue;
|
|
126
98
|
// Expiring a dead control is not a retry, so it ignores the backoff.
|
|
127
|
-
if (
|
|
99
|
+
if (isTerminal(run.state)) {
|
|
128
100
|
message.state = "expired";
|
|
129
101
|
message.error = "run finished before delivery completed";
|
|
130
102
|
this.store.saveMessage(message);
|
|
131
103
|
this.changed(message);
|
|
132
104
|
continue;
|
|
133
105
|
}
|
|
134
|
-
// A reply that resumed a terminal run is the continuation's prompt, not a
|
|
135
|
-
// system input: the transcript read would re-inject it and start a turn no
|
|
136
|
-
// run owns. The continuation's `startedAt` is the proof instead.
|
|
137
|
-
if (message.kind === "reply" && isTerminal(run.state)) {
|
|
138
|
-
const resumed = message.resumeRunId ? this.store.getRun(message.resumeRunId) : undefined;
|
|
139
|
-
if (resumed?.startedAt)
|
|
140
|
-
this.delivered(message);
|
|
141
|
-
else if (!resumed)
|
|
142
|
-
this.abandon(message, `continuation ${message.resumeRunId ?? "(none)"} is gone`);
|
|
143
|
-
else if (isTerminal(resumed.state)) {
|
|
144
|
-
// Never started and never will: said now, not four minutes later at
|
|
145
|
-
// the ceiling. A duplicate report beats a special case that could
|
|
146
|
-
// suppress the only one.
|
|
147
|
-
this.abandon(message, `continuation ${resumed.id} ${resumed.state} before it started`);
|
|
148
|
-
}
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
106
|
if ((message.nextAttemptAt ?? 0) > now)
|
|
152
107
|
continue;
|
|
153
108
|
const target = message.toSessionId || run.targetSessionId;
|
|
@@ -155,76 +110,17 @@ export class TaskMessenger {
|
|
|
155
110
|
this.deliver(message, target);
|
|
156
111
|
}
|
|
157
112
|
}
|
|
158
|
-
|
|
159
|
-
* run) or resumes the session (terminal run). A decision steers the
|
|
160
|
-
* supervisor, or a blocked child would wait out its whole turn. */
|
|
161
|
-
async contact(run, fromSessionId, reason, content) {
|
|
162
|
-
if (!run.invokedBySessionId)
|
|
163
|
-
throw new Error("run has no supervisor session");
|
|
164
|
-
if (reason === "decision" && this.openDecisionId(run.id)) {
|
|
165
|
-
throw new Error("run already has a pending supervisor decision");
|
|
166
|
-
}
|
|
167
|
-
const message = this.create(run, reason, fromSessionId, run.invokedBySessionId, content, null);
|
|
168
|
-
this.changed(message);
|
|
169
|
-
this.deliver(message, run.invokedBySessionId);
|
|
170
|
-
return this.require(message.id);
|
|
171
|
-
}
|
|
172
|
-
async reply(questionId, fromSessionId, content) {
|
|
173
|
-
const question = this.require(questionId);
|
|
174
|
-
if (question.kind !== "decision")
|
|
175
|
-
throw new Error("message is not a decision request");
|
|
176
|
-
if (question.toSessionId !== fromSessionId)
|
|
177
|
-
throw new Error("only the addressed supervisor may reply");
|
|
178
|
-
const existing = this.store.listMessages(question.runId).find((m) => m.kind === "reply" && m.replyTo === question.id);
|
|
179
|
-
const text = bounded(content);
|
|
180
|
-
if (existing) {
|
|
181
|
-
if (existing.content !== text)
|
|
182
|
-
throw new Error("decision already answered with different content");
|
|
183
|
-
return existing;
|
|
184
|
-
}
|
|
185
|
-
if (question.state !== "delivered" && question.state !== "pending") {
|
|
186
|
-
throw new Error(`decision is ${question.state}`);
|
|
187
|
-
}
|
|
188
|
-
const run = this.store.getRun(question.runId);
|
|
189
|
-
if (!run)
|
|
190
|
-
throw new Error(`unknown task run: ${question.runId}`);
|
|
191
|
-
// A rejected continuation must leave the question answerable. Persist the
|
|
192
|
-
// answer and its queued run together, before either publishes or executes.
|
|
193
|
-
const { reply, continuation } = this.store.transact(() => {
|
|
194
|
-
const reply = this.create(run, "reply", fromSessionId, question.fromSessionId, text, question.id);
|
|
195
|
-
question.state = "answered";
|
|
196
|
-
question.answeredAt = Date.now();
|
|
197
|
-
this.store.saveMessage(question);
|
|
198
|
-
const continuation = isTerminal(run.state)
|
|
199
|
-
? this.prepareResume(run.id, this.format(reply, run), fromSessionId)
|
|
200
|
-
: undefined;
|
|
201
|
-
if (continuation) {
|
|
202
|
-
reply.resumeRunId = continuation.id;
|
|
203
|
-
this.store.saveMessage(reply);
|
|
204
|
-
}
|
|
205
|
-
return { reply, continuation };
|
|
206
|
-
});
|
|
207
|
-
this.changed(question);
|
|
208
|
-
this.changed(reply);
|
|
209
|
-
if (continuation)
|
|
210
|
-
this.startRun(continuation);
|
|
211
|
-
else if (run.targetSessionId)
|
|
212
|
-
this.deliver(reply, run.targetSessionId);
|
|
213
|
-
return this.require(reply.id);
|
|
214
|
-
}
|
|
215
|
-
create(run, kind, fromSessionId, toSessionId, content, replyTo) {
|
|
113
|
+
create(run, kind, fromSessionId, toSessionId, content) {
|
|
216
114
|
const message = {
|
|
217
115
|
id: newId(),
|
|
218
116
|
runId: run.id,
|
|
219
117
|
kind,
|
|
220
118
|
fromSessionId,
|
|
221
119
|
toSessionId,
|
|
222
|
-
replyTo,
|
|
223
120
|
state: "pending",
|
|
224
121
|
content: bounded(content),
|
|
225
122
|
createdAt: Date.now(),
|
|
226
123
|
deliveredAt: null,
|
|
227
|
-
answeredAt: null,
|
|
228
124
|
error: null,
|
|
229
125
|
attempts: 0,
|
|
230
126
|
nextAttemptAt: null,
|
|
@@ -247,56 +143,20 @@ export class TaskMessenger {
|
|
|
247
143
|
callbackAttempts: message.attempts,
|
|
248
144
|
callbackError: message.error,
|
|
249
145
|
callbackNextAttemptAt: message.nextAttemptAt,
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
...(message.kind === "follow_up"
|
|
146
|
+
// A steer interrupts; a follow-up lands only once the recipient runs out
|
|
147
|
+
// of tool calls.
|
|
148
|
+
...(message.kind === "follow_up" ? {} : { callbackMode: "steer" }),
|
|
253
149
|
};
|
|
254
150
|
}
|
|
255
|
-
/**
|
|
256
|
-
delivered(message) {
|
|
257
|
-
message.state = "delivered";
|
|
258
|
-
message.deliveredAt = Date.now();
|
|
259
|
-
message.error = null;
|
|
260
|
-
message.nextAttemptAt = null;
|
|
261
|
-
this.store.saveMessage(message);
|
|
262
|
-
this.changed(message);
|
|
263
|
-
}
|
|
264
|
-
abandon(message, why) {
|
|
265
|
-
message.state = "expired";
|
|
266
|
-
message.error = why;
|
|
267
|
-
message.answeredAt = Date.now();
|
|
268
|
-
message.nextAttemptAt = null;
|
|
269
|
-
this.store.saveMessage(message);
|
|
270
|
-
this.changed(message);
|
|
271
|
-
this.told(message, why);
|
|
272
|
-
}
|
|
273
|
-
/** Both ends are told, and an expired decision stops suppressing its run's
|
|
274
|
-
* completion callback, which `execution.ts` decided once and never revisits. */
|
|
151
|
+
/** Both ends are told. */
|
|
275
152
|
told(message, why) {
|
|
276
153
|
this.unreachable(message.toSessionId, `a ${message.kind} from run ${message.runId}`, why);
|
|
277
154
|
if (message.fromSessionId && message.fromSessionId !== message.toSessionId) {
|
|
278
155
|
this.unreachable(message.fromSessionId, `your ${message.kind} on run ${message.runId}`, why);
|
|
279
156
|
}
|
|
280
|
-
const run = this.store.getRun(message.runId);
|
|
281
|
-
if (message.kind !== "decision" || !run?.callbackSessionId)
|
|
282
|
-
return;
|
|
283
|
-
if (run.callbackState === null && isTerminal(run.state)) {
|
|
284
|
-
run.callbackState = "pending"; // the tick sweep delivers it
|
|
285
|
-
this.store.saveRun(run);
|
|
286
|
-
}
|
|
287
157
|
}
|
|
288
158
|
format(message, run) {
|
|
289
|
-
|
|
290
|
-
if (message.kind === "progress") {
|
|
291
|
-
return `Subagent progress for task "${title}"\nRun: ${run.id}\nMessage: ${message.id}\n\n${message.content}`;
|
|
292
|
-
}
|
|
293
|
-
if (message.kind === "decision") {
|
|
294
|
-
return `Subagent needs a decision for task "${title}"\nRun: ${run.id}\nMessage: ${message.id}\nReply with the Task reply operation.\n\n${message.content}`;
|
|
295
|
-
}
|
|
296
|
-
if (message.kind === "reply") {
|
|
297
|
-
return `Supervisor reply for task "${title}"\nRun: ${run.id}\nReply to: ${message.replyTo ?? "-"}\n\n${message.content}`;
|
|
298
|
-
}
|
|
299
|
-
return `Task guidance for "${title}"\nRun: ${run.id}\nMessage: ${message.id}\n\n${message.content}`;
|
|
159
|
+
return `Task guidance for "${run.context.definition.name}"\nRun: ${run.id}\nMessage: ${message.id}\n\n${message.content}`;
|
|
300
160
|
}
|
|
301
161
|
origin(message, run) {
|
|
302
162
|
return {
|