@gobing-ai/ts-ai-runner 0.3.1 → 0.3.3
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 +336 -32
- package/dist/agent-detector.d.ts +1 -0
- package/dist/agent-detector.d.ts.map +1 -1
- package/dist/agent-detector.js +13 -5
- package/dist/agent-spec.d.ts +6 -0
- package/dist/agent-spec.d.ts.map +1 -1
- package/dist/agent-spec.js +12 -8
- package/dist/ai-runner.d.ts +18 -2
- package/dist/ai-runner.d.ts.map +1 -1
- package/dist/ai-runner.js +52 -6
- package/dist/doctor-runner.d.ts +7 -0
- package/dist/doctor-runner.d.ts.map +1 -1
- package/dist/doctor-runner.js +69 -15
- package/dist/events.d.ts +38 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +0 -0
- package/dist/identity.d.ts +3 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +2 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/messages.d.ts +4 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +4 -0
- package/dist/team-agent-process.d.ts +12 -4
- package/dist/team-agent-process.d.ts.map +1 -1
- package/dist/team-agent-process.js +31 -19
- package/dist/team-orchestrator.d.ts +13 -8
- package/dist/team-orchestrator.d.ts.map +1 -1
- package/dist/team-orchestrator.js +32 -25
- package/package.json +4 -4
- package/src/agent-detector.ts +14 -5
- package/src/agent-spec.ts +20 -8
- package/src/ai-runner.ts +75 -13
- package/src/doctor-runner.ts +77 -16
- package/src/events.ts +25 -0
- package/src/identity.ts +3 -0
- package/src/index.ts +2 -1
- package/src/messages.ts +6 -0
- package/src/team-agent-process.ts +36 -21
- package/src/team-orchestrator.ts +36 -25
- package/dist/message-service.d.ts +0 -13
- package/dist/message-service.d.ts.map +0 -1
- package/dist/message-service.js +0 -27
- package/src/message-service.ts +0 -33
package/src/identity.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BunSyncProcessExecutor, type SyncProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
2
2
|
|
|
3
|
+
/** Context used to construct the identity preamble injected into agent prompts. */
|
|
3
4
|
export interface IdentityContext {
|
|
4
5
|
agentId: string;
|
|
5
6
|
agentType: string;
|
|
@@ -14,6 +15,7 @@ export interface IdentityContext {
|
|
|
14
15
|
guardrails?: string[];
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
/** Build a human-readable identity preamble string that describes the agent, its task, peers, guardrails, and git context. */
|
|
17
19
|
export function buildIdentityPreamble(ctx: IdentityContext): string {
|
|
18
20
|
const sections: string[] = [
|
|
19
21
|
`You are agent \`${ctx.agentId}\` (${ctx.agentType}) in workspace \`${ctx.workspace}\`.`,
|
|
@@ -62,6 +64,7 @@ export function buildIdentityPreamble(ctx: IdentityContext): string {
|
|
|
62
64
|
return `${sections.join('\n\n')}\n`;
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
/** Query git for the current branch name and dirty file count in `workspacePath`. Returns a pre-formatted "Git context" block, or `null` if git is unavailable or the directory is not a repo. */
|
|
65
68
|
export function getGitContext(
|
|
66
69
|
workspacePath: string,
|
|
67
70
|
executor: SyncProcessExecutor = new BunSyncProcessExecutor(),
|
package/src/index.ts
CHANGED
|
@@ -3,8 +3,9 @@ export * from './agent-spec';
|
|
|
3
3
|
export * from './agents/shims';
|
|
4
4
|
export * from './ai-runner';
|
|
5
5
|
export * from './doctor-runner';
|
|
6
|
+
export * from './events';
|
|
6
7
|
export * from './identity';
|
|
7
|
-
export * from './
|
|
8
|
+
export * from './messages';
|
|
8
9
|
export * from './slash-command';
|
|
9
10
|
export * from './team-agent-process';
|
|
10
11
|
export * from './team-orchestrator';
|
package/src/messages.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { InboxMessage } from '@gobing-ai/ts-db/inbox';
|
|
2
|
+
|
|
3
|
+
/** Renders an inbox message into the line injected into an agent's stdin. */
|
|
4
|
+
export function formatMessage(msg: InboxMessage): string {
|
|
5
|
+
return `[task from=${msg.fromId ?? 'operator'} id=${msg.id}] ${msg.body}`;
|
|
6
|
+
}
|
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
|
-
import {
|
|
2
|
+
import { getLogger, type Logger } from '@gobing-ai/ts-infra';
|
|
3
|
+
import { type PipeProcess, ProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
3
4
|
import type { AgentSpec } from './agent-spec';
|
|
4
|
-
import { buildIdentityPreamble } from './identity';
|
|
5
5
|
|
|
6
|
+
/** Options for spawning a team agent subprocess. */
|
|
6
7
|
export interface AgentProcessOptions {
|
|
7
8
|
spec: AgentSpec;
|
|
8
9
|
command: string[];
|
|
9
10
|
env?: Record<string, string>;
|
|
10
11
|
cwd?: string;
|
|
11
|
-
|
|
12
|
+
processExecutor?: ProcessExecutor;
|
|
13
|
+
logger?: Logger;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
type ProcessStatus = 'running' | 'stopped' | 'errored';
|
|
15
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Manages the lifecycle of a single agent subprocess — start, stop, message send, and stdout/stderr subscription.
|
|
20
|
+
* The identity preamble is built by `TeamOrchestrator` and baked into `command` before the process is constructed.
|
|
21
|
+
*/
|
|
16
22
|
export class TeamAgentProcess {
|
|
17
23
|
readonly agentId: string;
|
|
18
|
-
readonly identityPreamble: string;
|
|
19
24
|
private readonly command: string[];
|
|
20
25
|
private readonly env: Record<string, string> | undefined;
|
|
21
26
|
private readonly cwd: string | undefined;
|
|
22
|
-
private readonly
|
|
27
|
+
private readonly processExecutor: ProcessExecutor;
|
|
28
|
+
private readonly logger: Logger;
|
|
23
29
|
private subprocess: PipeProcess | null = null;
|
|
24
30
|
private status: ProcessStatus = 'stopped';
|
|
25
31
|
private exitCode: number | null = null;
|
|
@@ -27,27 +33,21 @@ export class TeamAgentProcess {
|
|
|
27
33
|
|
|
28
34
|
constructor(options: AgentProcessOptions) {
|
|
29
35
|
this.agentId = options.spec.id;
|
|
30
|
-
this.identityPreamble = buildIdentityPreamble({
|
|
31
|
-
agentId: options.spec.id,
|
|
32
|
-
agentType: options.spec.type,
|
|
33
|
-
workspace: options.spec.workspace,
|
|
34
|
-
purpose: options.spec.purpose,
|
|
35
|
-
systemPrompt:
|
|
36
|
-
typeof options.spec.config.systemPrompt === 'string' ? options.spec.config.systemPrompt : undefined,
|
|
37
|
-
});
|
|
38
36
|
this.command = options.command;
|
|
39
37
|
this.env = options.env;
|
|
40
38
|
this.cwd = options.cwd ?? options.spec.workspace;
|
|
41
|
-
this.
|
|
39
|
+
this.processExecutor = options.processExecutor ?? new ProcessExecutor();
|
|
40
|
+
this.logger = options.logger ?? getLogger('team-agent');
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
async start(): Promise<void> {
|
|
45
44
|
if (this.status === 'running') return;
|
|
46
45
|
const [command, ...args] = this.command;
|
|
47
46
|
if (command === undefined) throw new Error(`${this.agentId}: command must not be empty`);
|
|
48
|
-
this.subprocess = this.
|
|
47
|
+
this.subprocess = this.processExecutor.runStreaming({
|
|
49
48
|
command,
|
|
50
49
|
args,
|
|
50
|
+
label: `team-agent.${this.agentId}`,
|
|
51
51
|
...(this.cwd !== undefined ? { cwd: this.cwd } : {}),
|
|
52
52
|
...(this.env !== undefined ? { env: this.env } : {}),
|
|
53
53
|
});
|
|
@@ -69,11 +69,13 @@ export class TeamAgentProcess {
|
|
|
69
69
|
}
|
|
70
70
|
try {
|
|
71
71
|
process.endStdin();
|
|
72
|
-
} catch {
|
|
73
|
-
|
|
72
|
+
} catch (error) {
|
|
73
|
+
this.warn('stdin close failed', 'stop.endStdin', error);
|
|
74
74
|
}
|
|
75
75
|
process.kill('SIGTERM');
|
|
76
|
-
const timeout =
|
|
76
|
+
const timeout = new Promise<'timeout'>((resolve) => {
|
|
77
|
+
setTimeout(() => resolve('timeout'), 5000);
|
|
78
|
+
});
|
|
77
79
|
const result = await Promise.race([process.exited, timeout]);
|
|
78
80
|
if (result === 'timeout') {
|
|
79
81
|
process.kill('SIGKILL');
|
|
@@ -86,11 +88,15 @@ export class TeamAgentProcess {
|
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
async send(message: string): Promise<{ ok: boolean }> {
|
|
89
|
-
if (this.status !== 'running' || this.subprocess === null)
|
|
91
|
+
if (this.status !== 'running' || this.subprocess === null) {
|
|
92
|
+
this.warn('send skipped because process is not running', 'send.notRunning');
|
|
93
|
+
return { ok: false };
|
|
94
|
+
}
|
|
90
95
|
try {
|
|
91
96
|
this.subprocess.writeStdin(`${message}\n`);
|
|
92
97
|
return { ok: true };
|
|
93
|
-
} catch {
|
|
98
|
+
} catch (error) {
|
|
99
|
+
this.warn('stdin write failed', 'send.writeStdin', error);
|
|
94
100
|
this.status = 'errored';
|
|
95
101
|
return { ok: false };
|
|
96
102
|
}
|
|
@@ -124,10 +130,19 @@ export class TeamAgentProcess {
|
|
|
124
130
|
const buffer = Buffer.from(chunk.value);
|
|
125
131
|
for (const subscriber of this.subscribers) subscriber(buffer);
|
|
126
132
|
}
|
|
127
|
-
} catch {
|
|
133
|
+
} catch (error) {
|
|
134
|
+
this.warn('stream pipe failed', 'pipe', error);
|
|
128
135
|
if (this.status === 'running') this.status = 'errored';
|
|
129
136
|
} finally {
|
|
130
137
|
reader.releaseLock();
|
|
131
138
|
}
|
|
132
139
|
}
|
|
140
|
+
|
|
141
|
+
private warn(message: string, op: string, error?: unknown): void {
|
|
142
|
+
this.logger.warn(message, {
|
|
143
|
+
agentId: this.agentId,
|
|
144
|
+
op,
|
|
145
|
+
...(error !== undefined ? { error: error instanceof Error ? error.message : String(error) } : {}),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
133
148
|
}
|
package/src/team-orchestrator.ts
CHANGED
|
@@ -1,30 +1,38 @@
|
|
|
1
|
+
import type { InboxMessageDao } from '@gobing-ai/ts-db/inbox';
|
|
2
|
+
import { EventBus } from '@gobing-ai/ts-infra';
|
|
1
3
|
import type { AgentSpec } from './agent-spec';
|
|
2
4
|
import { loadAgentSpecs } from './agent-spec';
|
|
3
5
|
import { type AgentName, getAgentShim, isAgentName } from './agents/shims';
|
|
6
|
+
import type { AgentEvents } from './events';
|
|
4
7
|
import { buildIdentityPreamble } from './identity';
|
|
5
|
-
import {
|
|
8
|
+
import { formatMessage } from './messages';
|
|
6
9
|
import { TeamAgentProcess } from './team-agent-process';
|
|
7
10
|
|
|
8
|
-
type TeamEvent = 'agent.started' | 'agent.stopped' | 'message.sent';
|
|
9
|
-
type TeamListener = (payload: unknown) => void;
|
|
10
11
|
type AgentProcessFactory = (options: ConstructorParameters<typeof TeamAgentProcess>[0]) => TeamAgentProcess;
|
|
11
12
|
|
|
13
|
+
/** Configuration options for `TeamOrchestrator`. */
|
|
12
14
|
export interface TeamOrchestratorOptions {
|
|
13
15
|
processFactory?: AgentProcessFactory;
|
|
16
|
+
events?: EventBus<AgentEvents>;
|
|
14
17
|
}
|
|
15
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Orchestrates a team of AI agents — loads specs, starts/stops agent processes, routes messages between them,
|
|
21
|
+
* and emits lifecycle events. Agents in the same workspace see each other as peers.
|
|
22
|
+
*/
|
|
16
23
|
export class TeamOrchestrator {
|
|
17
24
|
private specs: AgentSpec[] = [];
|
|
18
25
|
private readonly running = new Map<string, TeamAgentProcess>();
|
|
19
|
-
private readonly listeners = new Map<TeamEvent, Set<TeamListener>>();
|
|
20
26
|
private readonly processFactory: AgentProcessFactory;
|
|
27
|
+
private readonly events: EventBus<AgentEvents>;
|
|
21
28
|
|
|
22
29
|
constructor(
|
|
23
30
|
private readonly configDir: string,
|
|
24
|
-
private readonly
|
|
31
|
+
private readonly inbox: InboxMessageDao,
|
|
25
32
|
options: TeamOrchestratorOptions = {},
|
|
26
33
|
) {
|
|
27
34
|
this.processFactory = options.processFactory ?? ((processOptions) => new TeamAgentProcess(processOptions));
|
|
35
|
+
this.events = options.events ?? new EventBus<AgentEvents>();
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
loadSpecs(): AgentSpec[] {
|
|
@@ -67,7 +75,11 @@ export class TeamOrchestrator {
|
|
|
67
75
|
await process.start();
|
|
68
76
|
this.running.set(id, process);
|
|
69
77
|
await this.injectPendingMessages(process);
|
|
70
|
-
this.emit('agent.started', {
|
|
78
|
+
void this.events.emit('agent.started', {
|
|
79
|
+
agentId: spec.id,
|
|
80
|
+
agentType: spec.type,
|
|
81
|
+
pid: process.getPid(),
|
|
82
|
+
});
|
|
71
83
|
return process;
|
|
72
84
|
}
|
|
73
85
|
|
|
@@ -76,7 +88,7 @@ export class TeamOrchestrator {
|
|
|
76
88
|
if (process === undefined) return;
|
|
77
89
|
await process.stop();
|
|
78
90
|
this.running.delete(id);
|
|
79
|
-
this.emit('agent.stopped', { id });
|
|
91
|
+
void this.events.emit('agent.stopped', { agentId: id, exitCode: process.getExitCode() });
|
|
80
92
|
}
|
|
81
93
|
|
|
82
94
|
async restartAgent(id: string): Promise<TeamAgentProcess> {
|
|
@@ -85,10 +97,10 @@ export class TeamOrchestrator {
|
|
|
85
97
|
}
|
|
86
98
|
|
|
87
99
|
async sendMessage(fromId: string | null, toId: string, body: string, inReplyTo?: string): Promise<string> {
|
|
88
|
-
const msgId = await this.
|
|
100
|
+
const msgId = await this.inbox.enqueue(fromId, toId, body, inReplyTo);
|
|
89
101
|
const process = this.running.get(toId);
|
|
90
|
-
|
|
91
|
-
this.emit('message.sent', {
|
|
102
|
+
const ok = process !== undefined ? await this.flushInbox(process, 'live stdin injection failed') : false;
|
|
103
|
+
void this.events.emit('agent.message.sent', { agentId: toId, ok });
|
|
92
104
|
return msgId;
|
|
93
105
|
}
|
|
94
106
|
|
|
@@ -111,11 +123,9 @@ export class TeamOrchestrator {
|
|
|
111
123
|
await Promise.all([...this.running.keys()].map((id) => this.stopAgent(id)));
|
|
112
124
|
}
|
|
113
125
|
|
|
114
|
-
on(event:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.listeners.set(event, listeners);
|
|
118
|
-
return () => listeners.delete(listener);
|
|
126
|
+
on<K extends keyof AgentEvents>(event: K, listener: AgentEvents[K]): () => void {
|
|
127
|
+
this.events.on(event, listener);
|
|
128
|
+
return () => this.events.off(event, listener);
|
|
119
129
|
}
|
|
120
130
|
|
|
121
131
|
private requireSpec(id: string): AgentSpec {
|
|
@@ -129,20 +139,21 @@ export class TeamOrchestrator {
|
|
|
129
139
|
return type;
|
|
130
140
|
}
|
|
131
141
|
|
|
132
|
-
private injectPendingMessages(process: TeamAgentProcess): Promise<
|
|
142
|
+
private async injectPendingMessages(process: TeamAgentProcess): Promise<boolean> {
|
|
133
143
|
return this.flushInbox(process, 'startup stdin injection failed');
|
|
134
144
|
}
|
|
135
145
|
|
|
136
|
-
private async flushInbox(process: TeamAgentProcess, failLabel: string): Promise<
|
|
137
|
-
const messages = await this.
|
|
146
|
+
private async flushInbox(process: TeamAgentProcess, failLabel: string): Promise<boolean> {
|
|
147
|
+
const messages = await this.inbox.drainPending(process.agentId);
|
|
148
|
+
let ok = true;
|
|
138
149
|
for (const message of messages) {
|
|
139
|
-
const result = await process.send(
|
|
140
|
-
if (result.ok) await this.
|
|
141
|
-
else
|
|
150
|
+
const result = await process.send(formatMessage(message));
|
|
151
|
+
if (result.ok) await this.inbox.markDelivered(message.id);
|
|
152
|
+
else {
|
|
153
|
+
ok = false;
|
|
154
|
+
await this.inbox.markFailed(message.id, failLabel);
|
|
155
|
+
}
|
|
142
156
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
private emit(event: TeamEvent, payload: unknown): void {
|
|
146
|
-
for (const listener of this.listeners.get(event) ?? []) listener(payload);
|
|
157
|
+
return ok;
|
|
147
158
|
}
|
|
148
159
|
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { InboxMessage, InboxMessageDao } from '@gobing-ai/ts-db/inbox';
|
|
2
|
-
export declare class MessageService {
|
|
3
|
-
private readonly dao;
|
|
4
|
-
constructor(dao: InboxMessageDao);
|
|
5
|
-
enqueue(fromId: string | null, toId: string, body: string, inReplyTo?: string): Promise<string>;
|
|
6
|
-
drain(toId: string): Promise<InboxMessage[]>;
|
|
7
|
-
deliver(msgId: string): Promise<void>;
|
|
8
|
-
fail(msgId: string, error: string): Promise<void>;
|
|
9
|
-
inbox(toId: string, limit?: number, offset?: number): Promise<InboxMessage[]>;
|
|
10
|
-
countPending(toId: string): Promise<number>;
|
|
11
|
-
static formatMessage(msg: InboxMessage): string;
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=message-service.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"message-service.d.ts","sourceRoot":"","sources":["../src/message-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE5E,qBAAa,cAAc;IACX,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,eAAe;IAEjD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI/F,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAI5C,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAI7E,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI3C,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM;CAGlD"}
|
package/dist/message-service.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
export class MessageService {
|
|
2
|
-
dao;
|
|
3
|
-
constructor(dao) {
|
|
4
|
-
this.dao = dao;
|
|
5
|
-
}
|
|
6
|
-
enqueue(fromId, toId, body, inReplyTo) {
|
|
7
|
-
return this.dao.enqueue(fromId, toId, body, inReplyTo);
|
|
8
|
-
}
|
|
9
|
-
drain(toId) {
|
|
10
|
-
return this.dao.drainPending(toId);
|
|
11
|
-
}
|
|
12
|
-
deliver(msgId) {
|
|
13
|
-
return this.dao.markDelivered(msgId);
|
|
14
|
-
}
|
|
15
|
-
fail(msgId, error) {
|
|
16
|
-
return this.dao.markFailed(msgId, error);
|
|
17
|
-
}
|
|
18
|
-
inbox(toId, limit, offset) {
|
|
19
|
-
return this.dao.inbox(toId, limit, offset);
|
|
20
|
-
}
|
|
21
|
-
countPending(toId) {
|
|
22
|
-
return this.dao.countPending(toId);
|
|
23
|
-
}
|
|
24
|
-
static formatMessage(msg) {
|
|
25
|
-
return `[task from=${msg.fromId ?? 'operator'} id=${msg.id}] ${msg.body}`;
|
|
26
|
-
}
|
|
27
|
-
}
|
package/src/message-service.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { InboxMessage, InboxMessageDao } from '@gobing-ai/ts-db/inbox';
|
|
2
|
-
|
|
3
|
-
export class MessageService {
|
|
4
|
-
constructor(private readonly dao: InboxMessageDao) {}
|
|
5
|
-
|
|
6
|
-
enqueue(fromId: string | null, toId: string, body: string, inReplyTo?: string): Promise<string> {
|
|
7
|
-
return this.dao.enqueue(fromId, toId, body, inReplyTo);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
drain(toId: string): Promise<InboxMessage[]> {
|
|
11
|
-
return this.dao.drainPending(toId);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
deliver(msgId: string): Promise<void> {
|
|
15
|
-
return this.dao.markDelivered(msgId);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
fail(msgId: string, error: string): Promise<void> {
|
|
19
|
-
return this.dao.markFailed(msgId, error);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
inbox(toId: string, limit?: number, offset?: number): Promise<InboxMessage[]> {
|
|
23
|
-
return this.dao.inbox(toId, limit, offset);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
countPending(toId: string): Promise<number> {
|
|
27
|
-
return this.dao.countPending(toId);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
static formatMessage(msg: InboxMessage): string {
|
|
31
|
-
return `[task from=${msg.fromId ?? 'operator'} id=${msg.id}] ${msg.body}`;
|
|
32
|
-
}
|
|
33
|
-
}
|