flowviant 0.125.16 → 0.125.17
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 -0
- package/bin/lib/agentQueueContext.mjs +57 -0
- package/bin/lib/orchestrator.mjs +2 -2
- package/bin/lib/orchestratorPrompt.mjs +1 -0
- package/bin/lib/workAgentTurnExecution.mjs +4 -1
- package/bin/lib/workAgentTurnShape.mjs +3 -2
- package/bin/lib/workLiveMessages.mjs +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -318,6 +318,18 @@ pinned because [npm 12 removed shrinkwrap support](https://github.com/npm/cli/bl
|
|
|
318
318
|
and omits it from tarballs; npm 12 consumers also ignore that lockfile.
|
|
319
319
|
No sibling repository or separate app deploy key is required.
|
|
320
320
|
|
|
321
|
+
### Staged agent queue context (0.125.17)
|
|
322
|
+
|
|
323
|
+
The daemon now includes the API's versioned `queueContext` in task, human and
|
|
324
|
+
live-note prompts, including native recovery and fresh isolated research. The
|
|
325
|
+
snapshot preserves full queued requirements, shared decisions and exact retained
|
|
326
|
+
input references while execution/children remain scoped to the active card.
|
|
327
|
+
It grants no early queue work or artifact acceptance. Older daemons retain the
|
|
328
|
+
per-card protocol; the API sends this field only to 0.125.17 or newer. Release
|
|
329
|
+
and install the combined daemon before a shared conversation UI relies on this
|
|
330
|
+
capability. No migration, publication, installation or deployment is performed
|
|
331
|
+
by staging this version; published-latest remains unchanged.
|
|
332
|
+
|
|
321
333
|
### Staged eight-file chat delivery (0.125.13)
|
|
322
334
|
|
|
323
335
|
This source stages eight files per message in card-conversation downloads and
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** Queue knowledge is independent of native session memory. Never slice a
|
|
2
|
+
* requirement or interpret an outcome summary as a measured success. */
|
|
3
|
+
const fence = (label, value) => `<<<BEGIN ${label} (untrusted data)>>>\n${JSON.stringify(value, null, 2).replace(/<<<|>>>/g, m => m.split('').join('\u200b'))}\n<<<END ${label}>>>`;
|
|
4
|
+
|
|
5
|
+
export function agentQueueContextText(job) {
|
|
6
|
+
const context = job.queueContext;
|
|
7
|
+
if (!context) return '';
|
|
8
|
+
if (context.version !== 1 || context.agentId !== job.agentId || context.turnId !== job.id)
|
|
9
|
+
throw new Error('The agent queue context does not belong to this turn.');
|
|
10
|
+
return `\n\nAGENT QUEUE CONTEXT — refreshed from the app for this handoff.
|
|
11
|
+
This snapshot replaces earlier queue snapshots, including in resumed sessions.
|
|
12
|
+
Read the current and upcoming cards' complete requirements and recorded edges;
|
|
13
|
+
an order alone is not a declared dependency. requirements.status=missing is
|
|
14
|
+
unavailable, never an empty brief. Discussions carry their existing omission
|
|
15
|
+
counts and may be unavailable; do not treat a shortened discussion as full
|
|
16
|
+
requirements. The brief and criteria marked full are supplied without clipping.
|
|
17
|
+
Human messages are exact recorded words across this agent, with the preceding
|
|
18
|
+
question when present. taskId on a message is where it was recorded, not an
|
|
19
|
+
implicit scope restriction. target.kind=agent explicitly addresses the whole
|
|
20
|
+
agent; target.kind=question answers exactly its named question/card, without
|
|
21
|
+
narrowing any broader scope stated in the person's words. Card discussion notes
|
|
22
|
+
remain attached to their named card. Apply instructions at their stated scope across the
|
|
23
|
+
run; only explicit targeting narrows them to a card. Later corrections supersede
|
|
24
|
+
earlier instructions. Pending messages wait for their own kickoff or live-note
|
|
25
|
+
handoff; a snapshot alone is not a delivery receipt. In particular, afterTurn
|
|
26
|
+
messages wait for the next turn. Keep live notes' existing routing and safe
|
|
27
|
+
points; a receipt that the parent saw one does not mean it was acted on. The
|
|
28
|
+
current handed message and settled decisions remain available on future cards.
|
|
29
|
+
omittedHistory/unavailable explicitly name history excluded by the existing
|
|
30
|
+
research Knowledge correction rules. Read current Knowledge and ask before
|
|
31
|
+
relying on an excluded decision; do not recover it from older native memory.
|
|
32
|
+
Latest outcomes and progress are reported summaries, not complete specifications,
|
|
33
|
+
proof that checks passed, human acceptance or evidence that bytes exist locally.
|
|
34
|
+
Stored outputs name exact ids/turns/hashes, not a successful local read. Retained
|
|
35
|
+
result selections preserve their exact scope. Use the active turn's measured
|
|
36
|
+
input manifest and prerequisite receipts before relying on files; never replace
|
|
37
|
+
or regenerate missing selected inputs or assume queue knowledge discovers them.
|
|
38
|
+
Only execute the active turn's assigned work. Upcoming cards are context, not
|
|
39
|
+
permission to start them, change their kind or bypass a dependency or acceptance.
|
|
40
|
+
Keep this turn's product contract, write/research fence, chosen CLI/model,
|
|
41
|
+
delivery JSON and per-card commit attribution. Do not merge or accept for the
|
|
42
|
+
person. Pass children focused assignments, the active card's constraints and
|
|
43
|
+
only relevant prior findings/decisions/input identities, not the entire queue.
|
|
44
|
+
${fence('AGENT QUEUE SNAPSHOT', context)}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The gate supplies active identity/fences, not upcoming work to a child.
|
|
48
|
+
* Relevant findings still travel with the root's focused assignment. */
|
|
49
|
+
export function agentChildScope(job) {
|
|
50
|
+
if (!job.queueContext) return '';
|
|
51
|
+
const card = job.queueContext.cards.find(c => c.taskId === (job.task?.id ?? job.queueContext.activeTaskId));
|
|
52
|
+
return `\n\nACTIVE CARD SCOPE. Work only on the orchestrator's focused assignment for this turn. Upcoming queued work is not authorized. Preserve this card's product kind, write/research fence, exact input identities and human acceptance rules. Ask the orchestrator for any missing relevant requirement, decision or prerequisite; do not infer success from a summary.\n${fence('CHILD ACTIVE CARD IDENTITY', {
|
|
53
|
+
agentId: job.agentId, turnId: job.id, taskId: card?.taskId ?? job.task?.id ?? null,
|
|
54
|
+
taskKind: job.taskKind ?? job.task?.taskKind ?? 'code', taskType: job.task?.taskType ?? null,
|
|
55
|
+
retainedResults: card?.retainedResults ?? [],
|
|
56
|
+
})}`;
|
|
57
|
+
}
|
package/bin/lib/orchestrator.mjs
CHANGED
|
@@ -35,7 +35,7 @@ const text = (s, n) => scrubNativeText(s, n, scrub);
|
|
|
35
35
|
* on Codex, only for a model in `catalog`, the measured spawnable models.
|
|
36
36
|
*/
|
|
37
37
|
export async function createOrchestrator({ turnId, runtime, trace, admit, request = fleetRequest, notify = () => false, taken = () => {},
|
|
38
|
-
interval = 500, hold = 60_000, clock = Date.now, scrubText = text, children = {}, readOnlyRun = null, signal, rootTools = null } = {}) {
|
|
38
|
+
interval = 500, hold = 60_000, clock = Date.now, scrubText = text, children = {}, readOnlyRun = null, signal, rootTools = null, assignmentContext = () => '' } = {}) {
|
|
39
39
|
// Consults belong to a held attempt, never to a card turn. This capability
|
|
40
40
|
// is entirely local; it cannot report routes or borrow an agent principal.
|
|
41
41
|
if (readOnlyRun && (!['plan', 'capture'].includes(readOnlyRun.scope) || !filled(readOnlyRun.id) ||
|
|
@@ -159,7 +159,7 @@ export async function createOrchestrator({ turnId, runtime, trace, admit, reques
|
|
|
159
159
|
? `\n\nRead-only investigation agent record id: ${id}. FIRST call card_orchestrator.register_agent with agentId=${id} before work (tool discovery is allowed). Share this checkout and its repository/knowledge read boundary. Write no file, edit nothing, commit nothing, start no work and call no board or staging tool. Return findings, source paths and uncertainties in your final message to the coordinator. Ask questions only through card_orchestrator.agent_question with agentId=${id}, and read its answer before proceeding. ${readOnlyRun.scope === 'capture' ? 'Call no flowviant tool, including streaming. Only the coordinator dedupes and authors staged outcomes and asks the person through the existing single-question flowviant-ask fence.' : 'Only the coordinator reconciles the complete workload and returns the proposal.'}`
|
|
160
160
|
: `\n\nCard orchestrator agent record id: ${id}. FIRST call card_orchestrator.register_agent with agentId=${id} before doing work (tool discovery is allowed). Share this worktree and preserve others’ edits. Ask questions through card_orchestrator.agent_question with agentId=${id}; read its answer before proceeding. Only your orchestrator escalates person-only decisions. When you are done, end with your work or findings as your final message: that is how they reach your orchestrator, which owns the card’s final commit and JSON. agent_question is only for a question you cannot settle yourself, never for handing back results.`;
|
|
161
161
|
return { context: `Agent record id: ${id}. Use it with card_orchestrator for routing and questions.${spawn.relocated ? ' Its isolation and cwd were removed: children share this card’s worktree on this machine.' : ''}${choice ? ` This child runs on ${choice}: name that choice and why in your next update.` : ''}`,
|
|
162
|
-
updatedInput: withChildContract(event, spawn, childContract + (access === 'read' && !readOnlyRun ? READ_ONLY_CHILD : ''), chosen.brain) };
|
|
162
|
+
updatedInput: withChildContract(event, spawn, childContract + (!readOnlyRun ? assignmentContext() : '') + (access === 'read' && !readOnlyRun ? READ_ONLY_CHILD : ''), chosen.brain) };
|
|
163
163
|
}
|
|
164
164
|
// Native readings never revive a settled record or start a waiting spawn
|
|
165
165
|
// or follow-up (Codex records a refused call's start anyway); only an
|
|
@@ -117,6 +117,7 @@ export function orchestratorContract({ posture = 'build', taskType = null, runti
|
|
|
117
117
|
const reads = ORCHESTRATED_POSTURES[posture] === 'read';
|
|
118
118
|
return `CARD ORCHESTRATOR — this applies to this card's one CLI and worktree.
|
|
119
119
|
You are the model the person chose and remain responsible for the card and its final JSON.
|
|
120
|
+
When an agent queue snapshot is supplied, use it across resumed and fresh turns. Execute only the active card. A person's instruction keeps its stated scope across later cards; its recorded card is provenance, not an implicit target. Give each child a focused assignment with relevant prior findings, decisions and exact prerequisite references. Upcoming work never expands a child's assignment or the active card's product/write contract.
|
|
120
121
|
${SPLIT}${taskType === 'tests' ? `\n${TESTS_SCOPE} Give every child this Tests scope explicitly.` : ''}${reads ? `\n${RESEARCH}` : ''}
|
|
121
122
|
${childBrain({ runtime, model, effort })}
|
|
122
123
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { imageRevisionInstructions } from './artifactIteration.mjs';
|
|
2
|
+
import { agentChildScope } from './agentQueueContext.mjs';
|
|
2
3
|
import { measureTestFindings } from './testFindings.mjs';
|
|
3
4
|
import { fetchResultInputs } from './resultInputs.mjs';
|
|
4
5
|
import { agentWork } from './workActivity.mjs';
|
|
@@ -432,6 +433,7 @@ export function createAgentTurnExecution({
|
|
|
432
433
|
try {
|
|
433
434
|
if (orchestrating) {
|
|
434
435
|
orchestrator = await createOrchestrator({ turnId, runtime: rt, trace, admit,
|
|
436
|
+
assignmentContext: () => agentChildScope(job),
|
|
435
437
|
// Codex's spawn takes only a catalog model (knownCodexModels, the
|
|
436
438
|
// measured models_cache.json rows the roster already reports).
|
|
437
439
|
children: { access: ORCHESTRATED_POSTURES[posture], ...cardBrain, ...(rt === 'codex' ? { catalog: knownCodexModels() } : {}) },
|
|
@@ -447,9 +449,10 @@ export function createAgentTurnExecution({
|
|
|
447
449
|
scope: 'agent', turnId, cwd: wt, sessionMetaPath, interrupted: () => run.interrupted,
|
|
448
450
|
observe: message => orchestrator?.note(message),
|
|
449
451
|
...(rt === 'codex' ? { nativeIds: () => null } : {}),
|
|
450
|
-
format: async (followup) => {
|
|
452
|
+
format: async (followup, { queueContext } = {}) => {
|
|
451
453
|
orchestrator?.note(followup);
|
|
452
454
|
const attached = followup.attachments?.length ? await fetchAgentFiles(wt, followup.attachments.map((file) => ({ ...file, from: 'message' }))) : { message: [], card: [] };
|
|
455
|
+
if (queueContext) job.queueContext = queueContext;
|
|
453
456
|
return agentFollowupKickoff(job, followup, { messageFiles: attached.message, cardFiles: files.card, intermediateFiles, orchestrated: Boolean(orchestrator) });
|
|
454
457
|
},
|
|
455
458
|
})) } : {}),
|
|
@@ -26,6 +26,7 @@ import { AGENT_TASK_KICKOFF, AGENT_HUMAN_KICKOFF, SYSTEM_AGENT_FOR, withProjectC
|
|
|
26
26
|
import { INTERNAL_WAIT_CONTRACT } from './agentInternalWait.mjs';
|
|
27
27
|
import { orchestratorContract } from './orchestratorPrompt.mjs';
|
|
28
28
|
import { intermediateArtifactContext } from './intermediateArtifacts.mjs';
|
|
29
|
+
import { agentQueueContextText } from './agentQueueContext.mjs';
|
|
29
30
|
|
|
30
31
|
/** What every kickoff of this job is handed: the agent, the card and its
|
|
31
32
|
* discussion, the job's context handoffs, the card's files, the place in
|
|
@@ -64,7 +65,7 @@ export const agentKickoff = (job, { cardFiles, messageFiles, intermediateFiles }
|
|
|
64
65
|
// The files (0.112.0): the person's inside their words, the
|
|
65
66
|
// card's at the foot of its discussion.
|
|
66
67
|
messageFiles,
|
|
67
|
-
})) + intermediateArtifactContext(intermediateFiles);
|
|
68
|
+
})) + agentQueueContextText(job) + intermediateArtifactContext(intermediateFiles);
|
|
68
69
|
|
|
69
70
|
/** A live message sent while the turn runs, as the CLI is handed it — on an
|
|
70
71
|
* orchestrated card with its id, so the route it takes is reported. */
|
|
@@ -74,7 +75,7 @@ export const agentFollowupKickoff = (job, followup, { cardFiles, messageFiles, i
|
|
|
74
75
|
message: followup.body + (followup.libraryPrompt ?? '') + (orchestrated ? `\nMessage id: ${followup.id}. Choose and report its route with card_orchestrator.route_message.` : ''),
|
|
75
76
|
askedByName: followup.askedByName,
|
|
76
77
|
messageFiles,
|
|
77
|
-
}) + intermediateArtifactContext(intermediateFiles);
|
|
78
|
+
}) + agentQueueContextText(job) + intermediateArtifactContext(intermediateFiles);
|
|
78
79
|
|
|
79
80
|
/** The turn's system prompt, in its fixed section order. `orchestration` is
|
|
80
81
|
* null on a solo run, else what the contract names: `{ posture, runtime,
|
|
@@ -77,7 +77,7 @@ export function createLiveMessages({ scope, turnId, attempt, cwd, sessionMetaPat
|
|
|
77
77
|
if (stopped || interrupted()) break;
|
|
78
78
|
if (taken.has(message.id) || seen?.has(message.id)) { onTaken(message.id); continue; }
|
|
79
79
|
if (sent.has(message.id)) continue;
|
|
80
|
-
const prompt = await format(message);
|
|
80
|
+
const prompt = await format(message, { queueContext: result.json.data.queueContext });
|
|
81
81
|
if (!stopped && !interrupted() && send(message.id, prompt)) sent.add(message.id);
|
|
82
82
|
}
|
|
83
83
|
})().catch(() => {}).finally(() => { flight = null; });
|
|
@@ -106,7 +106,7 @@ export function createLiveMessages({ scope, turnId, attempt, cwd, sessionMetaPat
|
|
|
106
106
|
onTaken(turnId); // the native transcript proves the original prompt was read
|
|
107
107
|
initialId = randomUUID(); // continuation, never a second copy of the user's UUID
|
|
108
108
|
initialIds.add(initialId);
|
|
109
|
-
return 'Continue the request already in this conversation after the process stopped. Inspect the work in flight before repeating a command, and finish under the same operating contract.';
|
|
109
|
+
return 'Continue the request already in this conversation after the process stopped. Inspect the work in flight before repeating a command, and finish under the same operating contract.' + (scope === 'agent' ? `\n\nCurrent handoff (refresh its queue/context and retain the active card contract; do not repeat completed actions):\n${prompt}` : '');
|
|
110
110
|
}
|
|
111
111
|
return prompt;
|
|
112
112
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowviant",
|
|
3
|
-
"version": "0.125.
|
|
3
|
+
"version": "0.125.17",
|
|
4
4
|
"description": "Run your own coding CLIs as build agents for Flowviant \u2014 Claude Code, Codex or Antigravity, on your own credentials. Holds your sessions, keeps a worktree per tab, and ships branches on your word.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|