@fieldwangai/agentflow 0.1.135 → 0.1.137

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.
@@ -15,8 +15,8 @@
15
15
  href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0"
16
16
  rel="stylesheet"
17
17
  />
18
- <script type="module" crossorigin src="/assets/index-CmpbCHAj.js"></script>
19
- <link rel="stylesheet" crossorigin href="/assets/index-BFQVTav-.css">
18
+ <script type="module" crossorigin src="/assets/index-DQvqqAeQ.js"></script>
19
+ <link rel="stylesheet" crossorigin href="/assets/index-CQsrSc3u.css">
20
20
  </head>
21
21
  <body>
22
22
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fieldwangai/agentflow",
3
- "version": "0.1.135",
3
+ "version": "0.1.137",
4
4
  "description": "Orchestration system for long-running complex agent tasks using Cursor, OpenCode, Claude Code, or Codex as execution backends",
5
5
  "type": "module",
6
6
  "main": "bin/agentflow.mjs",
@@ -120,7 +120,9 @@ node skills/agentflow-cli/scripts/agentflow-cli.mjs display-outputs --flow-id Te
120
120
 
121
121
  ## Workflow reporting
122
122
 
123
- The CLI implements `workflow-get` and `workflow-report`, but their state model, projection contract, concurrency rules, and AI procedure belong to the separate [`agentflow-workflow-report`](../agentflow-workflow-report/SKILL.md) skill. Use that skill whenever reading or mutating Workflow state; do not reconstruct the protocol from this general CLI guide.
123
+ The reusable transport lives in `scripts/workflow-report-client.mjs`. The CLI exposes it through `workflow-access-sync`, `workflow-get`, `workflow-report`, and `workflow-artifact-publish`; their permission model, state model, extension contract, concurrency rules, and AI procedure belong to the separate [`agentflow-workflow-report`](../agentflow-workflow-report/SKILL.md) skill. Use that skill whenever synchronizing access or reading/mutating Workflow state; do not reconstruct the protocol from this general CLI guide.
124
+
125
+ Every write requires the real business adapter `source`. Put the key-level `expectedVersions` map in the JSON file; use `absent` for a new resource key. `--expected-revision` is retained only for legacy whole-Workflow locking and should not be used by new integrations.
124
126
 
125
127
  ## Workflow
126
128
 
@@ -2,6 +2,7 @@
2
2
  import fs from "node:fs";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
+ import { createWorkflowReportClient } from "./workflow-report-client.mjs";
5
6
 
6
7
  const DEFAULT_BASE_URL = "http://ai.mengma.bigo.inner/";
7
8
  const DISPLAY_DEFINITION_KINDS = new Map([
@@ -41,7 +42,9 @@ Commands:
41
42
  display-outputs --flow-id <id> [--flow-source user]
42
43
  sync-workspace --workspace <id>
43
44
  workflow-get --workflow tapd:<id> [--flow-id <id>] [--runtime-only]
44
- workflow-report --workflow tapd:<id> --file <report.json> [--expected-revision <revision>]
45
+ workflow-access-sync --workflow tapd:<id> --file <access.json>
46
+ workflow-report --workflow tapd:<id> --file <report.json> [--source <adapter>] [--expected-revision <revision>] [--idempotency-key <key>]
47
+ workflow-artifact-publish --workflow tapd:<id> --file <artifact.json> [--source <adapter>] [--expected-revision <revision>] [--idempotency-key <key>]
45
48
  `;
46
49
  }
47
50
 
@@ -387,31 +390,77 @@ async function main() {
387
390
  const flowId = option(args, "flow-id") || option(args, "flow");
388
391
  const flowSource = option(args, "flow-source") || "user";
389
392
  const runtimeOnly = args["runtime-only"] === true || args.cached === true ? "1" : "";
390
- printJson(await httpJson(args, `/api/workflows/state${query({
393
+ const client = createWorkflowReportClient({ baseUrl: normalizedBaseUrl(args), token: authToken(args) });
394
+ printJson(await client.getState({
391
395
  workflow: workflow.key,
392
396
  flowId,
393
397
  flowSource,
394
- runtimeOnly,
395
- })}`));
398
+ runtimeOnly: runtimeOnly === "1",
399
+ }));
396
400
  return;
397
401
  }
398
402
 
399
403
  if (command === "workflow-report") {
400
404
  const body = readJsonFile(option(args, "file"));
401
- const workflow = workflowReferenceFromArgs(args, false) || parseWorkflowReference(body?.workflow?.key || "");
405
+ const workflow = workflowReferenceFromArgs(args, false) || parseWorkflowReference(
406
+ typeof body?.workflow === "string" ? body.workflow : body?.workflow?.key || "",
407
+ );
402
408
  if (!workflow && !(body?.workflow?.namespace && body?.workflow?.id)) {
403
409
  throw new Error("Missing workflow reference. Pass --workflow namespace:id or include workflow.namespace and workflow.id in the JSON file.");
404
410
  }
405
411
  if (workflow) body.workflow = workflow;
406
412
  const expectedRevision = option(args, "expected-revision");
407
413
  const idempotencyKey = option(args, "idempotency-key");
414
+ const reportSource = option(args, "source");
408
415
  const flowId = option(args, "flow-id") || option(args, "flow");
409
416
  const flowSource = option(args, "flow-source");
410
417
  if (expectedRevision) body.expectedRevision = expectedRevision;
411
418
  if (idempotencyKey) body.idempotencyKey = idempotencyKey;
419
+ if (reportSource) body.source = reportSource;
420
+ if (!String(body.source || "").trim()) throw new Error("Missing Workflow report source. Pass --source <adapter> or include source in the JSON file.");
412
421
  if (flowId) body.flowId = flowId;
413
422
  if (flowSource) body.flowSource = flowSource;
414
- printJson(await httpJson(args, "/api/workflows/report", { method: "POST", body }));
423
+ const client = createWorkflowReportClient({ baseUrl: normalizedBaseUrl(args), token: authToken(args) });
424
+ printJson(await client.report(body));
425
+ return;
426
+ }
427
+
428
+ if (command === "workflow-access-sync") {
429
+ const body = readJsonFile(option(args, "file"));
430
+ const workflow = workflowReferenceFromArgs(args, false) || parseWorkflowReference(
431
+ typeof body?.workflow === "string" ? body.workflow : body?.workflow?.key || "",
432
+ );
433
+ if (!workflow && !(body?.workflow?.namespace && body?.workflow?.id)) {
434
+ throw new Error("Missing workflow reference. Pass --workflow namespace:id or include workflow.namespace and workflow.id in the JSON file.");
435
+ }
436
+ if (workflow) body.workflow = workflow;
437
+ const client = createWorkflowReportClient({ baseUrl: normalizedBaseUrl(args), token: authToken(args) });
438
+ printJson(await client.syncAccess(body));
439
+ return;
440
+ }
441
+
442
+ if (command === "workflow-artifact-publish") {
443
+ const body = readJsonFile(option(args, "file"));
444
+ const workflow = workflowReferenceFromArgs(args, false) || parseWorkflowReference(
445
+ typeof body?.workflow === "string" ? body.workflow : body?.workflow?.key || "",
446
+ );
447
+ if (!workflow && !(body?.workflow?.namespace && body?.workflow?.id)) {
448
+ throw new Error("Missing workflow reference. Pass --workflow namespace:id or include workflow.namespace and workflow.id in the JSON file.");
449
+ }
450
+ if (workflow) body.workflow = workflow;
451
+ const expectedRevision = option(args, "expected-revision");
452
+ const idempotencyKey = option(args, "idempotency-key");
453
+ const reportSource = option(args, "source");
454
+ const flowId = option(args, "flow-id") || option(args, "flow");
455
+ const flowSource = option(args, "flow-source");
456
+ if (flowId) body.flowId = flowId;
457
+ if (flowSource) body.flowSource = flowSource;
458
+ if (expectedRevision) body.expectedRevision = expectedRevision;
459
+ if (idempotencyKey) body.idempotencyKey = idempotencyKey;
460
+ if (reportSource) body.source = reportSource;
461
+ if (!String(body.source || "").trim()) throw new Error("Missing Workflow artifact source. Pass --source <adapter> or include source in the JSON file.");
462
+ const client = createWorkflowReportClient({ baseUrl: normalizedBaseUrl(args), token: authToken(args) });
463
+ printJson(await client.publishArtifact(body));
415
464
  return;
416
465
  }
417
466
 
@@ -0,0 +1,71 @@
1
+ function cleanBaseUrl(value) {
2
+ return String(value || "").trim().replace(/\/+$/, "");
3
+ }
4
+
5
+ function workflowQuery(params = {}) {
6
+ const search = new URLSearchParams();
7
+ for (const [key, value] of Object.entries(params)) {
8
+ if (value === undefined || value === null || value === "") continue;
9
+ search.set(key, String(value));
10
+ }
11
+ const text = search.toString();
12
+ return text ? `?${text}` : "";
13
+ }
14
+
15
+ export function createWorkflowReportClient({ baseUrl, token, fetchImpl = globalThis.fetch } = {}) {
16
+ const origin = cleanBaseUrl(baseUrl);
17
+ const credential = String(token || "").trim();
18
+ if (!origin) throw new Error("Workflow Report client requires baseUrl");
19
+ if (!credential) throw new Error("Workflow Report client requires token");
20
+ if (typeof fetchImpl !== "function") throw new Error("Workflow Report client requires fetch");
21
+
22
+ const request = async (pathname, { method = "GET", body } = {}) => {
23
+ const url = new URL(pathname, `${origin}/`);
24
+ const headers = {
25
+ Accept: "application/json",
26
+ Authorization: `Bearer ${credential}`,
27
+ Cookie: `af_session=${encodeURIComponent(credential)}`,
28
+ };
29
+ if (body !== undefined) headers["Content-Type"] = "application/json";
30
+ const response = await fetchImpl(url, {
31
+ method,
32
+ headers,
33
+ body: body === undefined ? undefined : JSON.stringify(body),
34
+ });
35
+ const text = await response.text();
36
+ let data = null;
37
+ try {
38
+ data = text ? JSON.parse(text) : null;
39
+ } catch {
40
+ data = { text };
41
+ }
42
+ if (!response.ok) {
43
+ const message = data?.error || data?.message || text || `HTTP ${response.status}`;
44
+ const error = new Error(`${method} ${url.pathname} failed: ${message}`);
45
+ error.status = response.status;
46
+ error.data = data;
47
+ throw error;
48
+ }
49
+ return data;
50
+ };
51
+
52
+ return {
53
+ getState({ workflow, flowId = "", flowSource = "user", runtimeOnly = false } = {}) {
54
+ return request(`/api/workflows/state${workflowQuery({
55
+ workflow,
56
+ flowId,
57
+ flowSource,
58
+ runtimeOnly: runtimeOnly ? "1" : "",
59
+ })}`);
60
+ },
61
+ syncAccess(body = {}) {
62
+ return request("/api/workflows/access/sync", { method: "POST", body });
63
+ },
64
+ report(body = {}) {
65
+ return request("/api/workflows/report", { method: "POST", body });
66
+ },
67
+ publishArtifact(body = {}) {
68
+ return request("/api/workflow-artifacts/publish", { method: "POST", body });
69
+ },
70
+ };
71
+ }
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  name: agentflow-workflow-report
3
- description: Safely read, merge, and report AgentFlow Workflow actions, artifacts, producer-owned global state, and generic timeline projections through the AgentFlow CLI and HTTP protocol. Use when an AI agent or producer such as prd-flow needs to integrate Workflow reporting, publish progress or evidence, update globalState, assign version/sprint/milestone timeline membership, clear projections, or resolve revision and idempotency conflicts.
3
+ description: Safely synchronize TAPD-derived Workflow access, then read, merge, and report AgentFlow Workflow actions, artifacts, producer-owned global state, and generic timeline projections through the AgentFlow CLI and HTTP protocol. Use when an AI agent or producer such as prd-flow needs to integrate Workflow reporting, map TAPD Owner and participants, publish progress or evidence, update globalState, assign version/sprint/milestone timeline membership, clear projections, or resolve revision and idempotency conflicts.
4
4
  ---
5
5
 
6
6
  # AgentFlow Workflow Report
7
7
 
8
- Treat Workflow reporting as a read-modify-report protocol. Keep producer business state opaque to AgentFlow and publish only generic dashboard indexes through projections.
8
+ Treat Workflow reporting as one canonical producer-adapter protocol. The producer reports facts through `POST /api/workflows/report`; AgentFlow alone materializes and returns `snapshot`. Do not introduce producer-specific write endpoints for new integrations.
9
9
 
10
10
  ## Prerequisites
11
11
 
12
- Use the CLI bundled with the sibling `agentflow-cli` skill:
12
+ Use the Workflow Report client bundled with the sibling `agentflow-cli` skill. The CLI is its command-line wrapper for AI, scripts, and local verification:
13
13
 
14
14
  ```bash
15
15
  node skills/agentflow-cli/scripts/agentflow-cli.mjs <command> [options]
@@ -17,12 +17,13 @@ node skills/agentflow-cli/scripts/agentflow-cli.mjs <command> [options]
17
17
 
18
18
  If the script is unavailable, install `agentflow-cli` beside this skill. Require `AGENTFLOW_TOKEN` or `AGENTFLOW_SESSION_TOKEN`; never print either token. Use `AGENTFLOW_BASE_URL` only when overriding the default service.
19
19
 
20
- Read [references/protocol.md](references/protocol.md) completely before implementing a producer, changing the report contract, or constructing a payload beyond the quick pattern below.
20
+ Read [references/protocol.md](references/protocol.md) completely before implementing a producer, changing the report contract, constructing a payload, or answering questions about parameters, permissions, merge behavior, custom panels, and visible UI results.
21
21
 
22
22
  ## Required sequence
23
23
 
24
- 1. Resolve a canonical Workflow reference such as `tapd:1015046`.
25
- 2. Read the current materialized state and retain `snapshot.runtimeRevision`:
24
+ 1. Resolve a canonical Workflow reference such as `tapd:1015046`. The report schema is producer-generic, but the current AgentFlow identity adapter accepts only the `tapd` namespace. Do not claim that arbitrary Workflow namespaces already work.
25
+ 2. If the Adapter reads TAPD personnel, synchronize its authority snapshot with `POST /api/workflows/access/sync` before reporting: TAPD Owner becomes Workflow Owner and matched TAPD participants become derived Viewers. Keep this permission control-plane call separate from runtime report data.
26
+ 3. Read the current materialized state and retain `snapshot.resourceVersions` for every resource key the operation will touch:
26
27
 
27
28
  ```bash
28
29
  node skills/agentflow-cli/scripts/agentflow-cli.mjs workflow-get \
@@ -30,48 +31,70 @@ node skills/agentflow-cli/scripts/agentflow-cli.mjs workflow-get \
30
31
  --runtime-only
31
32
  ```
32
33
 
33
- 3. Compute only the intended semantic update.
34
- 4. Preserve unrelated `globalState` fields. Never infer or rewrite a producer's private schema.
35
- 5. When timeline membership changes, derive the complete current `projections.timeline` array from producer state. Use `[]` to clear it.
36
- 6. Write the payload to a JSON file and report it with the retained revision and a stable operation key:
34
+ 4. Compute only the intended semantic update. Choose one stable lowercase `source` for the business adapter (for example `prd-flow` or `release-bot`). `agentflow-cli` is only transport and must not replace the real producer identity.
35
+ 5. Preserve unrelated `globalState` fields. Never infer or rewrite a producer's private schema.
36
+ 6. When timeline membership changes, derive the complete producer-owned `projections.timeline` slice. AgentFlow preserves entries owned by other sources; use `[]` to clear only the current source's memberships.
37
+ 7. Put `expectedVersions` for every touched Action, Artifact, GlobalState path, Projection, Extension path, or Observation into the JSON payload. Use `"absent"` when creating a new key. Report it with a stable operation key:
37
38
 
38
39
  ```bash
39
40
  node skills/agentflow-cli/scripts/agentflow-cli.mjs workflow-report \
40
41
  --workflow tapd:1015046 \
41
42
  --file workflow-report.json \
42
- --expected-revision 'runtime:current-revision' \
43
43
  --idempotency-key 'implementation-finished:android:issue-2:v1'
44
44
  ```
45
45
 
46
- 7. On HTTP 409, fetch the latest state, reapply the intended semantic update, and retry once with the new revision. Never resend a stale full snapshot.
46
+ 8. On HTTP 409, refresh only the resource keys listed in `conflict.conflicts`, recompute the intended update, and retry once with their new versions. Never send a client field named `snapshot`; use `observation.state` for a complete producer observation and treat returned `snapshot` as server output.
47
47
 
48
48
  ## Report selection
49
49
 
50
50
  Include at least one capability:
51
51
 
52
+ - `observation`: report the producer's complete current observation when it computes a deterministic workflow view.
52
53
  - `action`: report a stable progress or lifecycle event.
53
54
  - `artifacts`: attach evidence; use stable artifact keys.
54
55
  - `globalState`: merge producer-owned durable state or remove explicit paths.
55
56
  - `projections`: replace generic derived indexes used by AgentFlow dashboards.
57
+ - `extensions`: report namespaced data for a registered specialized renderer, such as `extensions["prd-flow"].issues`.
58
+
59
+ Map data to the visible page deliberately:
60
+
61
+ - Global area: `observation.state`, incremental producer facts in `globalState`, and iteration membership in `projections.timeline`.
62
+ - Action timeline: stable `action.key` plus Action-scoped `artifacts`.
63
+ - Generic custom cards: use `globalState.sections` with built-in `text`, `user`, `chips`, `list`, and `link` field renderers.
64
+ - Specialized custom area: use namespaced `extensions` only when built-in renderers cannot express the layout. Saving an extension does not create a UI by itself; currently only `extensions["prd-flow"]` has a registered AI Docs / Issues renderer.
56
65
 
57
66
  Use projection-only reports when the producer state is already current and only dashboard membership needs synchronization.
58
67
 
68
+ For local Markdown or other content that must become a browser URL, publish it first with `workflow-artifact-publish` (`POST /api/workflow-artifacts/publish`), then use the returned Artifact in the same Workflow. Do not use `/api/prd-workflow/review-link` for new integrations.
69
+
59
70
  ## Non-negotiable rules
60
71
 
61
72
  - Keep `schemaVersion` at `1` unless the server advertises another version.
73
+ - Keep the runtime chain singular: producer adapter → Workflow Report client → AgentFlow. The Skill is guidance, not a transport hop.
62
74
  - Give every action a stable `key`.
75
+ - Send a stable lowercase `source` on every report and Markdown publish; it is required. Action, idempotency, Artifact, Projection, Extension, Observation, and GlobalState ownership are isolated by source-aware resource keys.
63
76
  - Give every timeline entry stable `kind` and `id` values.
64
77
  - Treat `dimensions` as opaque facets; do not hardcode Android, iOS, version, or prd-flow fields into AgentFlow state.
65
78
  - Treat `globalState` as the source of truth owned by the producer; treat projections as replaceable derived views.
66
- - Send the complete current timeline array whenever changing it. Omitting `projections` means no projection change.
67
- - Use `expectedRevision` for state or projection changes and a stable `idempotencyKey` for every logical operation.
79
+ - Send the complete current source-owned timeline slice whenever changing it. Omitting `projections` means no projection change.
80
+ - Use `expectedVersions` for key-level concurrency and a stable `idempotencyKey` for every logical operation. `expectedRevision` remains a whole-Workflow compatibility lock only when `expectedVersions` is absent.
68
81
  - Do not include credentials, tokens, cookies, or private environment values in actions, artifacts, state, projections, or logs.
69
82
 
83
+ ## Permissions and overwrite semantics
84
+
85
+ - Treat TAPD personnel as derived authority when the Adapter can read them: TAPD Owner maps to Workflow Owner and registered TAPD participants map to Viewer.
86
+ - Keep explicit grants separate from derived TAPD membership. Allow Owner and explicit Reporter writes. Treat TAPD participant Viewer, explicit Viewer, same-team Viewer, share-link Viewer, and admin review as read-only. Accept legacy `editor` only as a compatibility alias for Reporter.
87
+ - `observation.state` replaces the complete previous observation for the same `clientId`.
88
+ - `globalState.patch` recursively merges objects; arrays and scalars replace; `null` and `remove` delete explicit paths. The first reporting source to write a path owns it; another source cannot overwrite an owned path.
89
+ - Reusing an `action.key` updates the same semantic stage. Do not create a new key for refreshes or retries.
90
+ - `projections.timeline` replaces only the current source's entries; AgentFlow preserves other sources atomically.
91
+ - `extensions` can update only `extensions[source]`; objects recursively merge, arrays/scalars replace, and `null` deletes producer-owned fields.
92
+ - Publishing Markdown creates or updates a preview Artifact and review copy; it does not confirm a document or advance an Action.
93
+
70
94
  ## Failure handling
71
95
 
72
96
  - Missing token: stop and ask the user to configure `AGENTFLOW_TOKEN`.
73
97
  - HTTP 401/403: stop; do not retry with a token printed in a command or answer.
74
- - HTTP 409: follow the single read-merge-retry sequence.
98
+ - HTTP 409: inspect `workflow-resource-conflict` or `workflow-resource-ownership-conflict`, refresh the listed keys, and follow the single read-merge-retry sequence.
75
99
  - HTTP 400: fix the payload against the protocol reference; do not weaken validation.
76
- - Replayed idempotency key: accept `alreadyApplied: true` as success.
77
-
100
+ - Replayed idempotency key from the same `source`: accept `alreadyApplied: true` as success. Markdown publish returns the previously created preview instead of creating another copy.