@qilin-agent/tool-workflow 0.0.0-stage → 3.1.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.
@@ -0,0 +1,411 @@
1
+ /**
2
+ * The model-facing `workflow` tool: run a JavaScript orchestration script that fans out
3
+ * subagents, and return the script's final value. It owns the model-facing schema and run lifecycle; script
4
+ * parsing, execution, caps, and cancellation live behind `ctx.workflowEngine`
5
+ * (`@qilin-agent/workflow`), so a hardened engine swaps in without touching what the model
6
+ * sees. Foreground execution awaits `run.result` and always disposes the run; non-completed reasons
7
+ * become tool errors. `run_in_background: true` instead registers the run as an owned `ctx.jobs` job
8
+ * and returns its id immediately — the job's output ring streams live progress, and the run's value
9
+ * arrives with the job's completion notice. Presentation is an args-only generic card
10
+ * titled from `meta.name`. Explicit-ask usage guidance is registered as the tool's own prompt
11
+ * section rather than deployment persona prose.
12
+ * @module @qilin-agent/tool-workflow
13
+ */
14
+ import z from '@qilin-agent/schemastery';
15
+ import { defineTool } from '@qilin-agent/tools';
16
+ import { createWorkflowRecordMirror } from "./record.js";
17
+ export const name = 'tool-workflow';
18
+ export const inject = ['tools', 'workflowEngine', 'systemPrompt'];
19
+ export const Config = z.object({
20
+ toolName: z.string().default('workflow'),
21
+ maxResultChars: z.natural().min(1).default(50_000),
22
+ enableRunInBackground: z.boolean().default(true),
23
+ });
24
+ /** Render a contained recording failure without trusting the thrown value. */
25
+ function renderRecordingError(error) {
26
+ try {
27
+ return String(error);
28
+ }
29
+ catch {
30
+ return '[unrenderable thrown value]';
31
+ }
32
+ }
33
+ /**
34
+ * Project active top-level workflow runs into their parent Sessions without
35
+ * letting recording failure affect tool execution.
36
+ */
37
+ function createWorkflowRecorder(ctx) {
38
+ const active = new Map();
39
+ const append = (session, type, data) => {
40
+ // These four package-owned events are all log-only. Narrowing the generic
41
+ // append face here discharges Session.append's conditional options tuple.
42
+ const appendRecord = session.append.bind(session);
43
+ try {
44
+ appendRecord(type, data);
45
+ return true;
46
+ }
47
+ catch (error) {
48
+ ctx.logger.warn(`tool-workflow: disabled durable record after ${type} append failed: ${renderRecordingError(error)}`);
49
+ return false;
50
+ }
51
+ };
52
+ ctx.on('workflow/agent-start', (info, agent) => {
53
+ const session = active.get(info.id);
54
+ if (session === undefined)
55
+ return;
56
+ const data = {
57
+ runId: info.id,
58
+ seq: agent.seq,
59
+ label: agent.label,
60
+ ...agent.phase === undefined ? {} : { phase: agent.phase },
61
+ childId: agent.childId,
62
+ };
63
+ if (!append(session, 'tool-workflow/agent-start', data))
64
+ active.delete(info.id);
65
+ });
66
+ ctx.on('workflow/agent-end', (info, agent) => {
67
+ const session = active.get(info.id);
68
+ if (session === undefined)
69
+ return;
70
+ const data = {
71
+ runId: info.id,
72
+ seq: agent.seq,
73
+ outcome: agent.outcome,
74
+ };
75
+ if (!append(session, 'tool-workflow/agent-end', data))
76
+ active.delete(info.id);
77
+ });
78
+ return {
79
+ start(session, run) {
80
+ if (append(session, 'tool-workflow/run-start', { runId: run.id, name: run.meta.name })) {
81
+ active.set(run.id, session);
82
+ }
83
+ },
84
+ finish(runId, stopReason) {
85
+ const session = active.get(runId);
86
+ if (session !== undefined)
87
+ append(session, 'tool-workflow/run-end', { runId, stopReason });
88
+ active.delete(runId);
89
+ },
90
+ abandon: (runId) => { active.delete(runId); },
91
+ };
92
+ }
93
+ /**
94
+ * The script-authoring contract, embedded in the tool description. This IS the
95
+ * model-facing spec: the meta block, the hooks and their exact semantics, and
96
+ * the supported schema subset.
97
+ */
98
+ const DESCRIPTION = `Run a JavaScript workflow script that orchestrates subagents at scale. Use this for work that fans out across many independent pieces — an audit over many files, a migration, multi-angle research, adversarial verification of findings — where you write the orchestration as a script instead of delegating turn by turn.
99
+
100
+ The workflow's identity rides the \`meta\` parameter as JSON: required \`name\` (short kebab-case) and \`description\` strings, optional \`whenToUse\` string and \`phases\` array (\`{title, detail?, provider?, model?}\`). The \`script\` parameter is the plain JavaScript body ONLY (NOT TypeScript, and NO \`export const meta\` statement — meta is a parameter, not code), running with top-level await; end with \`return <value>\` — the value must be JSON-serializable and is this tool's result.
101
+
102
+ Script-body hooks:
103
+ - \`agent(prompt, opts?): Promise<any>\` — run one subagent to completion. Without \`opts.schema\` it resolves to the child's final text; with \`opts.schema\` (an object-rooted JSON Schema using ONLY type/properties/required/additionalProperties/items/enum/const/oneOf — no pattern/format/numeric bounds) it resolves to the validated object. Resolves \`null\` when the child fails (filter with \`.filter(Boolean)\`). Other opts: \`label\` (display), \`phase\` (progress group), and independent \`provider\`/\`model\` LLM target overrides (either may be provided alone). Anything else (\`effort\`/\`isolation\`/\`agentType\`) is rejected loudly.
104
+ - \`pipeline(items, ...stages): Promise<any[]>\` — run each item through the stages independently with NO barrier between stages (prefer this for multi-stage work). Each stage receives \`(prev, item, index)\`. An ordinary stage throw drops that ITEM to \`null\` and skips its remaining stages.
105
+ - \`parallel(thunks): Promise<any[]>\` — run zero-argument functions concurrently and await ALL of them (a barrier; use only when a stage genuinely needs every prior result together). A throwing thunk resolves to \`null\`.
106
+ - \`phase(title)\` — start a progress phase; \`log(message)\` — narrate progress; \`args\` — the tool call's \`args\` input, verbatim.
107
+
108
+ Misused hooks (bad arguments, unknown options, unsupported schemas, tripped caps) throw errors that ALWAYS kill the script — they never dissolve into a per-item \`null\`.
109
+
110
+ Constraints: concurrency and total-agent caps apply; no filesystem, network, timers, or Node.js APIs are provided — the agents do the work, the script only coordinates them.`;
111
+ /** The pending-state card: a generic card titled by the workflow's meta name. */
112
+ function presentWorkflowCall(args) {
113
+ return {
114
+ card: 'generic',
115
+ title: `workflow: ${args.meta.name}`,
116
+ rawInput: args.script,
117
+ };
118
+ }
119
+ /** The completed-state card: keep the pending title; render the result content as-is. */
120
+ function presentWorkflowResult(args, result) {
121
+ void args;
122
+ void result;
123
+ return { card: 'generic' };
124
+ }
125
+ /** A non-`completed` stop reason means the script did not finish cleanly. */
126
+ function stopReasonError(result) {
127
+ switch (result.stopReason) {
128
+ case 'completed':
129
+ return undefined;
130
+ case 'cancelled':
131
+ return `workflow run was cancelled${result.error !== undefined ? ` (${result.error})` : ''}`;
132
+ case 'error':
133
+ return `workflow run failed: ${result.error ?? 'unknown error'}`;
134
+ /* v8 ignore start -- defensive: WorkflowStopReason is a closed union, exhaustive by construction; a future variant fails here loudly */
135
+ default:
136
+ return `workflow run ended abnormally (${String(result.stopReason)})`;
137
+ /* v8 ignore stop */
138
+ }
139
+ }
140
+ /** Render the run's outcome text: the meta name, agent count, and the JSON value (capped). */
141
+ function renderResult(name, agentsStarted, value, maxChars) {
142
+ // The engine returns JSON data (null for a valueless script), so stringify never yields undefined.
143
+ const rendered = JSON.stringify(value, null, 2);
144
+ const clipped = rendered.length > maxChars
145
+ ? `${rendered.slice(0, maxChars)}\n… [truncated: ${rendered.length - maxChars} more characters]`
146
+ : rendered;
147
+ return `workflow "${name}" completed (${agentsStarted} agent${agentsStarted === 1 ? '' : 's'}).\nReturn value:\n${clipped}`;
148
+ }
149
+ /**
150
+ * Map a settled background run onto the job outcome vocabulary. A completed
151
+ * run carries the rendered return value as the job's result; a cancelled run
152
+ * leaves the detail to the registry's kill-reason merge (the cancel reason it
153
+ * forwarded is the same string); an errored run fails with the script's
154
+ * failure message.
155
+ * @param result - settled engine result.
156
+ * @param name - the workflow's meta name, for the rendered result.
157
+ * @param maxChars - rendered-result ceiling in characters.
158
+ * @returns the outcome for the job registration.
159
+ */
160
+ function jobOutcomeOf(result, name, maxChars) {
161
+ switch (result.stopReason) {
162
+ case 'completed':
163
+ return {
164
+ status: 'completed',
165
+ detail: `${result.agentsStarted} agent${result.agentsStarted === 1 ? '' : 's'}`,
166
+ result: renderResult(name, result.agentsStarted, result.value, maxChars),
167
+ };
168
+ case 'cancelled':
169
+ return { status: 'killed' };
170
+ case 'error':
171
+ return { status: 'failed', detail: result.error ?? 'unknown error' };
172
+ /* v8 ignore start -- defensive: WorkflowStopReason is a closed union, exhaustive by construction; a future variant fails here loudly */
173
+ default:
174
+ return { status: 'failed', detail: `workflow run ended abnormally (${String(result.stopReason)})` };
175
+ /* v8 ignore stop */
176
+ }
177
+ }
178
+ /**
179
+ * Register a background run as an owned job. The engine run is started inside
180
+ * the job starter with no tool-step signal — the run belongs to the job, so a
181
+ * registry kill or owner teardown is what cancels it — and its settlement is
182
+ * the job's settlement: dispose, stop the mirrors, then map the stop reason
183
+ * onto the job outcome (a completed run's rendered return value rides
184
+ * `result` to the model's first read after settlement).
185
+ * @param ctx - plugin context (engine, optional jobs registry, logger).
186
+ * @param args - the validated tool call.
187
+ * @param parent - the calling agent; owns the job.
188
+ * @param recordsRun - whether this top-level call records durable run events.
189
+ * @param deps - the tool's recorder/mirror taps and the render cap.
190
+ * @returns the background result for the tool's output schema.
191
+ */
192
+ function startBackgroundRun(ctx, args, parent, recordsRun, deps) {
193
+ const jobs = ctx.get('jobs');
194
+ if (jobs === undefined) {
195
+ throw new Error('background jobs unavailable: load @qilin-agent/jobs and @qilin-agent/tool-jobs');
196
+ }
197
+ let run;
198
+ const jobId = jobs.start({
199
+ kind: 'workflow',
200
+ label: args.meta.name,
201
+ owner: parent.id,
202
+ run: (job) => {
203
+ // A synchronous engine rejection (META_INVALID/SCRIPT_PARSE) propagates
204
+ // out of the starter, so the registry registers nothing and the model
205
+ // sees the violation list as an ordinary tool error.
206
+ run = ctx.workflowEngine.start({
207
+ script: args.script,
208
+ meta: args.meta,
209
+ ...args.args !== undefined ? { args: args.args } : {},
210
+ parent,
211
+ });
212
+ deps.mirror.start(run.id, job);
213
+ if (recordsRun)
214
+ deps.recorder.start(parent.session, run);
215
+ const done = run.result.then(async (result) => {
216
+ try {
217
+ // Keep member listeners alive through disposal: an engine may
218
+ // synthesize cancelled member endings while reaching quiescence.
219
+ await run.dispose();
220
+ }
221
+ catch (error) {
222
+ // done must not reject; a failed disposal still has a settled result to report.
223
+ ctx.logger.warn(`background workflow run ${run.id} dispose failed: ${String(error)}`);
224
+ }
225
+ deps.mirror.stop(run.id);
226
+ if (recordsRun) {
227
+ deps.recorder.finish(run.id, result.stopReason);
228
+ deps.recorder.abandon(run.id);
229
+ }
230
+ return jobOutcomeOf(result, args.meta.name, deps.maxResultChars);
231
+ });
232
+ return {
233
+ cancel: (reason) => { run.cancel(reason ?? 'background workflow job killed'); },
234
+ done,
235
+ };
236
+ },
237
+ });
238
+ return { kind: 'background', jobId, runId: run.id };
239
+ }
240
+ export function apply(ctx, config) {
241
+ // schemastery (the exported Config schema) has already filled the defaulted
242
+ // fields; the assertion records that resolution, not a hidden fallback.
243
+ const { toolName, maxResultChars, enableRunInBackground } = config;
244
+ const recorder = createWorkflowRecorder(ctx);
245
+ const mirror = createWorkflowRecordMirror(ctx);
246
+ // Usage policy ships with the tool (the master convention: tool guidance
247
+ // lives in tool plugins as prompt sections, not in the deployment persona).
248
+ ctx.systemPrompt.section({
249
+ name: `tool:${toolName}`,
250
+ order: ctx.systemPrompt.getSectionOrder('TOOL_WORKFLOW'),
251
+ text: `Use the ${toolName} tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.`,
252
+ });
253
+ ctx.tools.register(defineTool({
254
+ name: toolName,
255
+ description: DESCRIPTION,
256
+ parameters: {
257
+ script: {
258
+ type: 'string',
259
+ required: true,
260
+ description: 'The plain-JS workflow script body (top-level await allowed; NO `export const meta` statement; end with `return <json-value>`).',
261
+ },
262
+ meta: {
263
+ type: 'object',
264
+ additionalProperties: true,
265
+ required: true,
266
+ description: 'The workflow identity block (plain JSON — never code).',
267
+ properties: {
268
+ name: { type: 'string', required: true, description: 'Short kebab-case workflow name.' },
269
+ description: { type: 'string', required: true, description: 'One-line description of what the workflow does.' },
270
+ whenToUse: { type: 'string', description: 'Optional guidance on when this workflow applies.' },
271
+ phases: {
272
+ type: 'array',
273
+ description: 'Optional phase declarations matched by phase() calls.',
274
+ items: {
275
+ type: 'object',
276
+ additionalProperties: true,
277
+ properties: {
278
+ title: { type: 'string', required: true, description: 'The phase title phase() calls match by exact string.' },
279
+ detail: { type: 'string', description: 'Optional one-line description of the phase.' },
280
+ provider: { type: 'string', description: 'Optional provider override this phase is expected to use.' },
281
+ model: { type: 'string', description: 'Optional model override this phase is expected to use.' },
282
+ },
283
+ },
284
+ },
285
+ },
286
+ },
287
+ args: {
288
+ type: 'object',
289
+ additionalProperties: true,
290
+ description: 'Optional JSON input exposed to the script as the `args` global (wrap a bare list as a field, e.g. {"files": [...]}).',
291
+ },
292
+ ...enableRunInBackground ? {
293
+ run_in_background: {
294
+ type: 'boolean',
295
+ description: 'Run as a background job: return a job id immediately instead of waiting; the return value arrives with the completion notice.',
296
+ },
297
+ } : {},
298
+ },
299
+ output: {
300
+ schema: {
301
+ oneOf: [
302
+ {
303
+ type: 'object',
304
+ additionalProperties: false,
305
+ properties: {
306
+ kind: { type: 'string', required: true, const: 'background' },
307
+ jobId: { type: 'string', required: true },
308
+ runId: { type: 'string', required: true },
309
+ },
310
+ },
311
+ {
312
+ type: 'object',
313
+ additionalProperties: false,
314
+ properties: {
315
+ kind: { type: 'string', required: true, const: 'foreground' },
316
+ runId: { type: 'string', required: true },
317
+ agentsStarted: { type: 'integer', required: true },
318
+ result: { type: 'json', required: true },
319
+ },
320
+ },
321
+ ],
322
+ },
323
+ render: (args, value) => [{
324
+ type: 'text',
325
+ text: value.kind === 'background'
326
+ ? `workflow "${args.meta.name}" started in the background as job ${value.jobId}. Its return value arrives with the completion notice; check on it with job_output, stop it with job_kill.`
327
+ : renderResult(args.meta.name, value.agentsStarted, value.result, maxResultChars),
328
+ }],
329
+ },
330
+ async execute(args, exec) {
331
+ const parent = exec.agent;
332
+ if (!parent) {
333
+ // The loop sets `exec.agent` for every model-driven call; its absence
334
+ // means a non-agent caller invoked the tool directly, which has no
335
+ // parent to attribute the children to. Fail loud rather than guess.
336
+ throw new Error('workflow tool requires a calling agent (exec.agent was undefined)');
337
+ }
338
+ if (args.run_in_background === true) {
339
+ if (!enableRunInBackground) {
340
+ throw new Error('run_in_background is disabled for this tool');
341
+ }
342
+ // No pre-abort check here, unlike bash/pwsh: ToolRuntime re-reads the
343
+ // caller signal right before execute(), and this branch reaches
344
+ // jobs.start synchronously from there. The shell tools await a
345
+ // sandbox escalation approval before registering, which is the window
346
+ // their check covers.
347
+ return startBackgroundRun(ctx, args, parent, exec.parent === undefined, {
348
+ recorder,
349
+ mirror,
350
+ maxResultChars,
351
+ });
352
+ }
353
+ // Meta/body validation failures (META_INVALID/SCRIPT_PARSE) throw
354
+ // synchronously here and become isError results via the registry — the
355
+ // model sees the violation list and can correct the call.
356
+ const run = ctx.workflowEngine.start({
357
+ script: args.script,
358
+ meta: args.meta,
359
+ ...args.args !== undefined ? { args: args.args } : {},
360
+ parent,
361
+ signal: exec.signal,
362
+ });
363
+ const recordsRun = exec.parent === undefined;
364
+ // The engine publishes member events after start() returns and this run record is active.
365
+ if (recordsRun)
366
+ recorder.start(parent.session, run);
367
+ // Bridge the tool's abort signal to the run: if the parent step is aborted while the
368
+ // script is in flight, cancel the whole run. The signal also enters the engine directly, but
369
+ // this local bridge preserves the tool contract even if an implementation ignores it.
370
+ const onAbort = () => { run.cancel('parent step aborted'); };
371
+ exec.signal.addEventListener('abort', onAbort, { once: true });
372
+ let result;
373
+ try {
374
+ result = await run.result;
375
+ const error = stopReasonError(result);
376
+ if (error !== undefined) {
377
+ // Map a non-clean finish to an isError result (the registry turns a
378
+ // throw into an isError). Report the reason, not partial output.
379
+ throw new Error(error);
380
+ }
381
+ return {
382
+ kind: 'foreground',
383
+ runId: run.id,
384
+ agentsStarted: result.agentsStarted,
385
+ result: result.value,
386
+ };
387
+ }
388
+ finally {
389
+ exec.signal.removeEventListener('abort', onAbort);
390
+ try {
391
+ // Keep member listeners alive through disposal: an engine may
392
+ // synthesize cancelled member endings while reaching quiescence.
393
+ await run.dispose();
394
+ if (recordsRun) {
395
+ /* v8 ignore next -- WorkflowRun.result never rejects by contract, so result is assigned before finally. */
396
+ if (result === undefined)
397
+ throw new Error('workflow run settled without a result');
398
+ recorder.finish(run.id, result.stopReason);
399
+ }
400
+ }
401
+ finally {
402
+ if (recordsRun)
403
+ recorder.abandon(run.id);
404
+ }
405
+ }
406
+ },
407
+ presentCall: args => presentWorkflowCall(args),
408
+ presentResult: (args, result) => presentWorkflowResult(args, result),
409
+ }));
410
+ }
411
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Live-progress mirror for background workflow runs: streams the engine's
3
+ * `workflow/phase`, `workflow/log`, and member lifecycle events into the
4
+ * owning job's output ring as `log` chunks — observer-only narration the
5
+ * model's `job_output` never renders — and keeps the job's live progress
6
+ * line on the current phase. Appends against a settled job log and drop
7
+ * inside the registry, so a straggling event after settlement is harmless.
8
+ * @module @qilin-agent/tool-workflow/record
9
+ */
10
+ import type { Context } from '@qilin-agent/kylin';
11
+ import type { JobHandle } from '@qilin-agent/jobs';
12
+ import type { WorkflowRunId } from '@qilin-agent/workflow';
13
+ /** Job-ring taps for the background runs the tool tracks. */
14
+ export interface WorkflowRecordMirror {
15
+ /**
16
+ * Route a run's progress events into a job's ring. Call once per background
17
+ * run, from the job starter, before the worker publishes its first event.
18
+ * @param runId - the started run.
19
+ * @param job - the owning job's producer face.
20
+ */
21
+ start(runId: WorkflowRunId, job: JobHandle): void;
22
+ /**
23
+ * Stop routing a settled or abandoned run. Idempotent.
24
+ * @param runId - the run to drop.
25
+ */
26
+ stop(runId: WorkflowRunId): void;
27
+ }
28
+ /**
29
+ * Create the run-to-ring mirror and subscribe the engine's live progress
30
+ * events for the runs it tracks.
31
+ * @param ctx - plugin context whose event bus carries the `workflow/*` events.
32
+ * @returns the mirror taps the tool wires around each background run.
33
+ */
34
+ export declare function createWorkflowRecordMirror(ctx: Context): WorkflowRecordMirror;
35
+ //# sourceMappingURL=record.d.ts.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Live-progress mirror for background workflow runs: streams the engine's
3
+ * `workflow/phase`, `workflow/log`, and member lifecycle events into the
4
+ * owning job's output ring as `log` chunks — observer-only narration the
5
+ * model's `job_output` never renders — and keeps the job's live progress
6
+ * line on the current phase. Appends against a settled job log and drop
7
+ * inside the registry, so a straggling event after settlement is harmless.
8
+ * @module @qilin-agent/tool-workflow/record
9
+ */
10
+ /**
11
+ * Create the run-to-ring mirror and subscribe the engine's live progress
12
+ * events for the runs it tracks.
13
+ * @param ctx - plugin context whose event bus carries the `workflow/*` events.
14
+ * @returns the mirror taps the tool wires around each background run.
15
+ */
16
+ export function createWorkflowRecordMirror(ctx) {
17
+ const active = new Map();
18
+ ctx.on('workflow/phase', (info, title) => {
19
+ const job = active.get(info.id);
20
+ if (job === undefined)
21
+ return;
22
+ job.updateProgress(title);
23
+ job.append(`▸ ${title}\n`, { channel: 'log' });
24
+ });
25
+ ctx.on('workflow/log', (info, message) => {
26
+ active.get(info.id)?.append(`${message}\n`, { channel: 'log' });
27
+ });
28
+ ctx.on('workflow/agent-start', (info, agent) => {
29
+ active.get(info.id)?.append(`agent #${agent.seq} ${agent.label} started\n`, { channel: 'log' });
30
+ });
31
+ ctx.on('workflow/agent-end', (info, agent) => {
32
+ active.get(info.id)?.append(`agent #${agent.seq} ${agent.outcome}\n`, { channel: 'log' });
33
+ });
34
+ return {
35
+ start(runId, job) {
36
+ active.set(runId, job);
37
+ },
38
+ stop(runId) {
39
+ active.delete(runId);
40
+ },
41
+ };
42
+ }
43
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Browser-safe durable workflow-record events written by the model-facing
3
+ * workflow tool into its calling parent Session.
4
+ *
5
+ * @module @qilin-agent/tool-workflow/types
6
+ */
7
+ import type { SessionId } from '@qilin-agent/session/types';
8
+ import type { WorkflowAgentOutcome, WorkflowRunId, WorkflowStopReason } from '@qilin-agent/workflow/types';
9
+ /** Opens one durable top-level workflow run record. */
10
+ export interface ToolWorkflowRunStartData {
11
+ readonly runId: WorkflowRunId;
12
+ readonly name: string;
13
+ }
14
+ /** Records one workflow member after its child Session is published. */
15
+ export interface ToolWorkflowAgentStartData {
16
+ readonly runId: WorkflowRunId;
17
+ readonly seq: number;
18
+ readonly label: string;
19
+ readonly phase?: string;
20
+ readonly childId: SessionId;
21
+ }
22
+ /** Settles one previously started workflow member. */
23
+ export interface ToolWorkflowAgentEndData {
24
+ readonly runId: WorkflowRunId;
25
+ readonly seq: number;
26
+ readonly outcome: WorkflowAgentOutcome;
27
+ }
28
+ /** Settles one workflow run after its live resources reach quiescence. */
29
+ export interface ToolWorkflowRunEndData {
30
+ readonly runId: WorkflowRunId;
31
+ readonly stopReason: WorkflowStopReason;
32
+ }
33
+ declare module '@qilin-agent/session/types' {
34
+ interface SessionEventMap {
35
+ /**
36
+ * Opens one top-level workflow record.
37
+ * @param data - stable run identity and display name.
38
+ */
39
+ 'tool-workflow/run-start': ToolWorkflowRunStartData;
40
+ /**
41
+ * Records one published workflow member.
42
+ * @param data - run identity, member sequence, display identity, and child Session.
43
+ */
44
+ 'tool-workflow/agent-start': ToolWorkflowAgentStartData;
45
+ /**
46
+ * Records one member settlement.
47
+ * @param data - run identity, paired member sequence, and outcome.
48
+ */
49
+ 'tool-workflow/agent-end': ToolWorkflowAgentEndData;
50
+ /**
51
+ * Closes one workflow record after cleanup.
52
+ * @param data - stable run identity and terminal reason.
53
+ */
54
+ 'tool-workflow/run-end': ToolWorkflowRunEndData;
55
+ }
56
+ }
57
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Browser-safe durable workflow-record events written by the model-facing
3
+ * workflow tool into its calling parent Session.
4
+ *
5
+ * @module @qilin-agent/tool-workflow/types
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,6 +1,61 @@
1
1
  {
2
2
  "name": "@qilin-agent/tool-workflow",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "description": "Model-facing workflow tool: run a JavaScript orchestration script over ctx.workflowEngine",
4
+ "version": "3.1.3",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/kkutysllb/QiLin.git",
11
+ "directory": "packages/workflow/tool-workflow"
12
+ },
13
+ "engines": {
14
+ "node": "^22.19.0 || >=24.0.0"
15
+ },
16
+ "type": "module",
17
+ "main": "lib/index.js",
18
+ "types": "lib/types/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./lib/types/index.d.ts",
22
+ "default": "./lib/index.js"
23
+ },
24
+ "./types": {
25
+ "types": "./lib/types/types.d.ts",
26
+ "default": "./lib/types/types.js"
27
+ },
28
+ "./src/*": "./src/*",
29
+ "./package.json": "./package.json"
30
+ },
31
+ "files": [
32
+ "lib/index.js",
33
+ "lib/types/**/*.js",
34
+ "lib/types/**/*.d.ts"
35
+ ],
36
+ "license": "MIT",
37
+ "peerDependencies": {
38
+ "@qilin-agent/agent": "3.1.3",
39
+ "@qilin-agent/session": "3.1.3",
40
+ "@qilin-agent/llm": "3.1.3",
41
+ "@qilin-agent/system-prompt": "3.1.3",
42
+ "@qilin-agent/tools": "3.1.3",
43
+ "@qilin-agent/kylin": "~4.0.3",
44
+ "@qilin-agent/workflow": "3.1.3"
45
+ },
46
+ "dependencies": {
47
+ "@qilin-agent/schemastery": "~3.18.3"
48
+ },
49
+ "devDependencies": {
50
+ "@qilin-agent/agent": "3.1.3",
51
+ "@qilin-agent/llm": "3.1.3",
52
+ "@qilin-agent/session": "3.1.3",
53
+ "@qilin-agent/subagent": "3.1.3",
54
+ "@qilin-agent/system-prompt": "3.1.3",
55
+ "@qilin-agent/tools": "3.1.3",
56
+ "@qilin-agent/workflow": "3.1.3",
57
+ "@qilin-agent/workflow-ptc": "3.1.3",
58
+ "@qilin-agent/kylin": "~4.0.3",
59
+ "@qilin-agent/session-projection": "3.1.3"
60
+ }
6
61
  }