@mingchuno/agent-workflows 0.1.0 → 0.3.0

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.
Files changed (76) hide show
  1. package/README.md +37 -6
  2. package/dist/src/adapters/agents.js +6 -3
  3. package/dist/src/adapters/hosting.js +16 -10
  4. package/dist/src/adapters/sdk-protocol.d.ts +3 -3
  5. package/dist/src/adapters/sdk-protocol.js +9 -7
  6. package/dist/src/attribution.d.ts +9 -0
  7. package/dist/src/attribution.js +57 -0
  8. package/dist/src/cli.d.ts +1 -1
  9. package/dist/src/cli.js +80 -25
  10. package/dist/src/config.d.ts +24 -30
  11. package/dist/src/config.js +32 -27
  12. package/dist/src/defaults.d.ts +2 -0
  13. package/dist/src/defaults.js +2 -0
  14. package/dist/src/domain.d.ts +31 -4
  15. package/dist/src/domain.js +10 -3
  16. package/dist/src/evidence.d.ts +54 -0
  17. package/dist/src/evidence.js +214 -0
  18. package/dist/src/index.d.ts +1 -0
  19. package/dist/src/index.js +1 -0
  20. package/dist/src/invocation.d.ts +25 -0
  21. package/dist/src/invocation.js +166 -0
  22. package/dist/src/operations.d.ts +8 -2
  23. package/dist/src/operations.js +100 -136
  24. package/dist/src/prompts.d.ts +28 -0
  25. package/dist/src/prompts.js +63 -0
  26. package/dist/src/recovery.d.ts +19 -0
  27. package/dist/src/recovery.js +99 -0
  28. package/dist/src/runner.d.ts +7 -0
  29. package/dist/src/runner.js +170 -18
  30. package/dist/src/runtime/process.d.ts +2 -0
  31. package/dist/src/runtime/process.js +41 -12
  32. package/dist/src/store.d.ts +21 -2
  33. package/dist/src/store.js +122 -1
  34. package/dist/src/tui/actions.d.ts +16 -0
  35. package/dist/src/tui/actions.js +23 -0
  36. package/dist/src/tui/constants.d.ts +6 -0
  37. package/dist/src/tui/constants.js +3 -0
  38. package/dist/src/{tui-data.d.ts → tui/data.d.ts} +10 -7
  39. package/dist/src/tui/data.js +146 -0
  40. package/dist/src/tui/dialogs.d.ts +17 -0
  41. package/dist/src/tui/dialogs.js +149 -0
  42. package/dist/src/tui/format.d.ts +7 -0
  43. package/dist/src/tui/format.js +62 -0
  44. package/dist/src/tui/index.d.ts +3 -0
  45. package/dist/src/tui/index.js +2 -0
  46. package/dist/src/tui/layout.d.ts +25 -0
  47. package/dist/src/tui/layout.js +36 -0
  48. package/dist/src/tui/log-file.d.ts +26 -0
  49. package/dist/src/tui/log-file.js +156 -0
  50. package/dist/src/tui/log.d.ts +11 -0
  51. package/dist/src/tui/log.js +90 -0
  52. package/dist/src/tui/monitor.d.ts +10 -0
  53. package/dist/src/tui/monitor.js +284 -0
  54. package/dist/src/tui/notifications.d.ts +23 -0
  55. package/dist/src/tui/notifications.js +104 -0
  56. package/dist/src/tui/text.d.ts +3 -0
  57. package/dist/src/tui/text.js +10 -0
  58. package/dist/src/tui/use-log-controller.d.ts +27 -0
  59. package/dist/src/tui/use-log-controller.js +192 -0
  60. package/dist/src/tui/views.d.ts +25 -0
  61. package/dist/src/tui/views.js +327 -0
  62. package/dist/src/workspace.js +21 -8
  63. package/docs/api.md +132 -8
  64. package/docs/architecture.md +21 -4
  65. package/docs/configuration.md +181 -8
  66. package/docs/database.md +7 -0
  67. package/docs/operations.md +160 -5
  68. package/docs/providers.md +58 -2
  69. package/docs/releases.md +34 -79
  70. package/examples/config.ts +6 -6
  71. package/examples/run.ts +4 -1
  72. package/package.json +4 -2
  73. package/dist/src/tui-data.js +0 -89
  74. package/dist/src/tui.d.ts +0 -5
  75. package/dist/src/tui.js +0 -69
  76. package/docs/acceptance.md +0 -35
@@ -1,13 +1,13 @@
1
1
  import { configSchema } from "@mingchuno/agent-workflows";
2
2
 
3
- /** Replace paths, repository, identity and model IDs with locally available values. */
3
+ /** Replace paths, repository and model IDs with locally available values. */
4
4
  export const configuration = configSchema.parse({
5
5
  id: "local",
6
- stateDirectory: "/absolute/path/outside-the-checkout/agent-workflows-state",
6
+ stateDirectory: "../application.agent-workflows",
7
7
  projects: [
8
8
  {
9
9
  id: "application",
10
- checkout: "/absolute/path/to/existing-checkout",
10
+ checkout: ".",
11
11
  hosting: {
12
12
  provider: "gitlab",
13
13
  origin: "https://git.example.com/gitlab",
@@ -17,13 +17,13 @@ export const configuration = configSchema.parse({
17
17
  labels: ["ready-for-agent"],
18
18
  baseBranch: "main",
19
19
  branchTemplate: "agent/{issue}-{attempt}",
20
- gitIdentity: { name: "Workflow Agent", email: "agent@example.com" },
20
+ includeAgentCoAuthors: true,
21
21
  agent: { provider: "codex" },
22
22
  stages: {
23
23
  implementation: {
24
24
  prompt: "Implement the issue and follow repository guidance.",
25
25
  },
26
- writing: {
26
+ publication: {
27
27
  profile: { provider: "copilot" },
28
28
  prompt:
29
29
  "Write concise publication text from the diff and validation evidence.",
@@ -45,7 +45,7 @@ export const modelOverrides = {
45
45
  model: "YOUR_CODEX_MODEL",
46
46
  reasoningEffort: "high",
47
47
  },
48
- writing: {
48
+ publication: {
49
49
  provider: "copilot" as const,
50
50
  model: "YOUR_COPILOT_MODEL",
51
51
  reasoningEffort: "low",
package/examples/run.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { readFile } from "node:fs/promises";
2
+ import { dirname, resolve } from "node:path";
2
3
  import {
3
4
  configSchema,
4
5
  createAgents,
@@ -7,13 +8,15 @@ import {
7
8
  } from "@mingchuno/agent-workflows";
8
9
  import { reportingWorkflow } from "./custom-workflow.js";
9
10
 
11
+ const configPath = resolve(process.argv[2] ?? "agent-workflows.json");
10
12
  const config = configSchema.parse(
11
- JSON.parse(await readFile(process.argv[2] ?? "agent-workflows.json", "utf8")),
13
+ JSON.parse(await readFile(configPath, "utf8")),
12
14
  );
13
15
  const databaseUrl = process.env[config.databaseUrlEnv];
14
16
  if (!databaseUrl) throw new Error(`Set ${config.databaseUrlEnv}`);
15
17
  const runner = new Runner({
16
18
  config,
19
+ pathBaseDirectory: dirname(configPath),
17
20
  databaseUrl,
18
21
  hosting: createHosting,
19
22
  agents: createAgents(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mingchuno/agent-workflows",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Local durable coding workflows on DBOS",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,7 +40,7 @@
40
40
  "pack:smoke": "pnpm build && node scripts/test.mjs --pack-smoke",
41
41
  "release:publish": "node scripts/publish.mjs",
42
42
  "lint:pr": "node scripts/lint-pr-title.mjs",
43
- "start": "tsx src/cli.ts",
43
+ "start": "node --import tsx -- src/cli.ts",
44
44
  "format": "biome format --write .",
45
45
  "format:check": "biome format .",
46
46
  "lint": "biome check .",
@@ -60,6 +60,8 @@
60
60
  "pg": "^8.23.0",
61
61
  "pino": "^10.3.1",
62
62
  "react": "^19.3.0",
63
+ "string-width": "^8.2.2",
64
+ "wrap-ansi": "^9.0.2",
63
65
  "zod": "^4.6.5"
64
66
  },
65
67
  "devDependencies": {
@@ -1,89 +0,0 @@
1
- import { useEffect, useState } from "react";
2
- export function useMonitorData(source, selection) {
3
- const { projectIndex, runIndex } = selection;
4
- const [projects, setProjects] = useState([]);
5
- const [runs, setRuns] = useState([]);
6
- const [sessions, setSessions] = useState([]), [events, setEvents] = useState([]);
7
- const [message, setMessage] = useState("Connecting…"), [pending, setPending] = useState();
8
- const project = projects[projectIndex];
9
- const projectRuns = runs.filter((run) => run.projectId === project?.id);
10
- const run = projectRuns[runIndex];
11
- const selectedRunId = run?.id;
12
- useEffect(() => {
13
- let closed = false, busy = false;
14
- const update = async () => {
15
- if (busy)
16
- return;
17
- busy = true;
18
- try {
19
- const [nextProjects, nextRuns] = await Promise.all([
20
- source.projects(),
21
- source.runs(),
22
- ]);
23
- if (closed)
24
- return;
25
- setProjects(nextProjects);
26
- setRuns(nextRuns);
27
- if (selectedRunId) {
28
- const [nextSessions, nextEvents] = await Promise.all([
29
- source.invocations(selectedRunId),
30
- source.events(0, selectedRunId),
31
- ]);
32
- if (closed)
33
- return;
34
- setSessions(nextSessions);
35
- setEvents(nextEvents.filter((event) => event.runId === selectedRunId));
36
- }
37
- else {
38
- setSessions([]);
39
- setEvents([]);
40
- }
41
- if (pending) {
42
- const command = (await source.commands()).find((command) => command.id === pending);
43
- if (command && command.status !== "pending") {
44
- setMessage(`${command.kind}: ${command.status}${command.error ? ` — ${command.error}` : ""}`);
45
- setPending(undefined);
46
- }
47
- }
48
- else
49
- setMessage((current) => current === "Connecting…" ? "Connected" : current);
50
- }
51
- catch (error) {
52
- if (!closed)
53
- setMessage(`Connection error: ${String(error)}`);
54
- }
55
- finally {
56
- busy = false;
57
- }
58
- };
59
- void update();
60
- const timer = setInterval(() => void update(), 400);
61
- return () => {
62
- closed = true;
63
- clearInterval(timer);
64
- };
65
- }, [source, selectedRunId, pending]);
66
- const action = async (kind, target) => {
67
- setMessage(`${kind}: pending`);
68
- setPending("submitting");
69
- try {
70
- setPending(await source.request(kind, target));
71
- }
72
- catch (error) {
73
- setPending(undefined);
74
- setMessage(`${kind}: failed — ${String(error)}`);
75
- }
76
- };
77
- return {
78
- projects,
79
- project,
80
- projectRuns,
81
- run,
82
- sessions,
83
- events,
84
- message,
85
- setMessage,
86
- pending,
87
- action,
88
- };
89
- }
package/dist/src/tui.d.ts DELETED
@@ -1,5 +0,0 @@
1
- import { type MonitorSource } from "./tui-data.js";
2
- export type { MonitorSource } from "./tui-data.js";
3
- export declare function Monitor({ source }: {
4
- source: MonitorSource;
5
- }): import("react").JSX.Element;
package/dist/src/tui.js DELETED
@@ -1,69 +0,0 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { readFile } from "node:fs/promises";
3
- import { Box, Text, useApp, useInput } from "ink";
4
- import { useState } from "react";
5
- import { useMonitorData } from "./tui-data.js";
6
- export function Monitor({ source }) {
7
- const { exit } = useApp();
8
- const [projectIndex, setProjectIndex] = useState(0), [runIndex, setRunIndex] = useState(0), [sessionIndex, setSessionIndex] = useState(0), [stepIndex, setStepIndex] = useState(0), [validationIndex, setValidationIndex] = useState(0);
9
- const { projects, project, projectRuns, run, sessions, events, message, setMessage, pending, action, } = useMonitorData(source, { projectIndex, runIndex });
10
- const [log, setLog] = useState("");
11
- const session = sessions[sessionIndex];
12
- const steps = events.filter((event) => event.kind === "step");
13
- const selectedStep = steps[stepIndex];
14
- const showLog = (path) => {
15
- void readFile(path, "utf8").then((text) => setLog(text.split("\n").slice(-8).join("\n")), (error) => setMessage(`Log unavailable: ${String(error)}`));
16
- };
17
- useInput((input, key) => {
18
- if (input === "q") {
19
- exit();
20
- return;
21
- }
22
- if (key.leftArrow || key.rightArrow) {
23
- setProjectIndex((index) => Math.max(0, Math.min(projects.length - 1, index + (key.rightArrow ? 1 : -1))));
24
- setRunIndex(0);
25
- setSessionIndex(0);
26
- setStepIndex(0);
27
- setValidationIndex(0);
28
- setLog("");
29
- }
30
- if (key.upArrow || key.downArrow) {
31
- setRunIndex((index) => Math.max(0, Math.min(projectRuns.length - 1, index + (key.downArrow ? 1 : -1))));
32
- setSessionIndex(0);
33
- setStepIndex(0);
34
- setValidationIndex(0);
35
- setLog("");
36
- }
37
- if (key.tab) {
38
- setSessionIndex((index) => (index + 1) % Math.max(sessions.length, 1));
39
- setLog("");
40
- }
41
- if (input === "l" && session)
42
- showLog(session.log);
43
- if (input === "[" || input === "]")
44
- setStepIndex((index) => Math.max(0, Math.min(steps.length - 1, index + (input === "]" ? 1 : -1))));
45
- if (input === "v" && run?.validation?.length) {
46
- const check = run.validation[validationIndex % run.validation.length];
47
- setMessage(`Validation: ${check.command} · exit ${check.exitCode}`);
48
- showLog(check.log);
49
- setValidationIndex((index) => index + 1);
50
- }
51
- if (pending)
52
- return;
53
- if (input === "p" && project)
54
- void action(project.paused ? "resume" : "pause", project.id);
55
- if (input === "s" && run)
56
- void action("stop", run.id);
57
- if (input === "r" && run)
58
- void action("retry", run.id);
59
- });
60
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, children: "Agent Workflows \u00B7 Monitor" }), _jsx(Text, { children: "\u2190 \u2192 project \u00B7 \u2191 \u2193 run \u00B7 [ ] step/attempt \u00B7 Tab session \u00B7 l agent log \u00B7 v validation log \u00B7 p pause/resume \u00B7 s stop \u00B7 r retry \u00B7 q close" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, children: project
61
- ? `${project.id} · ${project.paused ? "intake paused" : "intake enabled"} · ${projectRuns.filter((run) => run.outcome === "queued").length} queued`
62
- : "No projects registered. Start the runner to populate this view." }), project?.blocked && (_jsxs(Text, { color: "yellow", children: ["Blocked: ", project.blocked] })), projectRuns
63
- .slice(Math.max(0, runIndex - 1), runIndex + 2)
64
- .map((item) => (_jsxs(Text, { inverse: item.id === run?.id, children: [item.id === run?.id ? ">" : " ", " #", item.issue.number, " \u00B7 attempt", " ", item.attempt, " \u00B7 ", item.outcome, " \u00B7 ", item.phase] }, item.id))), project && projectRuns.length === 0 && (_jsx(Text, { children: "No runs yet. Eligible issues appear after polling." }))] }), run && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { bold: true, children: ["Run ", run.id] }), _jsx(Text, { children: run.issue.title }), run.error && _jsx(Text, { color: "red", children: run.error }), _jsxs(Text, { children: ["Validation:", " ", run.validation
65
- ?.map((check) => `${check.command}: exit ${check.exitCode}`)
66
- .join(" · ") || "No checks recorded"] }), _jsxs(Text, { children: ["Step/attempt event ", steps.length ? stepIndex + 1 : 0, "/", steps.length, ":", " ", selectedStep
67
- ? JSON.stringify(selectedStep.payload)
68
- : "No steps recorded"] }), _jsxs(Text, { bold: true, children: ["Agent sessions \u00B7 ", sessions.length] }), session ? (_jsxs(_Fragment, { children: [_jsxs(Text, { children: [session.step, " \u00B7 invocation ", session.attempt, " \u00B7", " ", session.outcome] }), _jsxs(Text, { children: ["Session: ", session.sessionId ?? session.sessionState] }), _jsxs(Text, { children: ["Requested: ", JSON.stringify(session.requested)] }), _jsxs(Text, { children: ["Effective: ", JSON.stringify(session.effective)] }), _jsxs(Text, { children: ["Log: ", session.log] })] })) : (_jsx(Text, { children: "No agent sessions recorded" })), log && _jsx(Text, { children: log })] })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: message }) }), _jsx(Text, { dimColor: true, children: "Closing this monitor leaves the runner and its tasks running." })] }));
69
- }
@@ -1,35 +0,0 @@
1
- # Phase 1 review guide
2
-
3
- Implementation scope is the unchanged [specification in issue #1](https://github.com/mingchuno/agent-workflows/issues/1). Review the public contracts first, then the workspace/recovery boundaries. No task-specific worktrees, clones, merges or automatic review/fix loops are implemented.
4
-
5
- | Acceptance area | Implementation / behavioral evidence |
6
- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7
- | Existing checkout, clean startup, branch policy and base transition | `ExistingCheckout`; real Git tests cover dirty files, collisions, unresolved Git operations, mutation detection and next-task base |
8
- | Durable default workflow and extension | `Operations`, `defaultWorkflow`; PostgreSQL runner tests and the executable reporting-workflow example |
9
- | Deduplication, one task/project, independent progress | Instance-qualified task keys, DBOS concurrency-one queues; all four agent/host combinations and multi-project tests |
10
- | GitHub/GitLab intake and publication | Octokit/Gitbeaker contracts; pagination, PR exclusion, label queries, custom GitLab root/credentials, draft MR and inline review fixtures |
11
- | Agent profiles, SDKs, prompts and sessions | Isolated SDK workers; profile/default isolation, supported-setting rejection, SDK argument/event contracts and invocation records |
12
- | Validation and generated publication | Actual Git diff/fingerprints, schema-checked text; failed validation, malformed text, no-change and timeout tests |
13
- | Recovery and external effects | Process-level interruptions after edits, commit, push, request creation and review publication; blocked ambiguous agent recovery and no duplicate publication effects |
14
- | Cancellation and ownership | Real stubborn child process, journals, local leases, advisory locks; cancellation/reuse and duplicate-owner tests |
15
- | Independent exact-revision review | Fresh sessions, published diff/head context, added-line mapping and stale-head rejection |
16
- | Public observability and controls | Store query/subscription, retained sessions, timestamps, artifacts, credential redaction, CLI JSON and explicit retries |
17
- | CLI/TUI | Init/error/help tests; keyboard step/session navigation, live state and action feedback; controls share application commands |
18
- | SDK documentation | Quickstart, configuration, API, providers, operations/recovery and type-checked examples; reporting example runs with controlled adapters |
19
-
20
- ## Local verification
21
-
22
- Run `pnpm check`, `pnpm test`, `pnpm build`, and `pnpm lint`. Tests create a disposable PostgreSQL database unless `TEST_DATABASE_URL` is set, use real temporary Git repositories, and use controlled agents/hosting endpoints. `pnpm audit --prod` checks runtime dependency advisories. The GitHub Actions job supplies the same fixture boundary on Linux.
23
-
24
- The implementation was verified locally on macOS with Node 24 and PostgreSQL 17. Remote CI has not run for this uncommitted working tree. No authenticated live Codex/Copilot model invocation, real GitHub publication or real GitLab instance smoke test was performed. Those require a deliberately chosen test repository and provider access and are separate from deterministic acceptance.
25
-
26
- ## Deliberate operational boundaries
27
-
28
- - Interrupted agent work blocks if ownership/state is ambiguous; it never starts another writer merely because DBOS recovered.
29
- - Explicit retry creates a new attempt from the base after the developer restores a clean checkout. It preserves the previous run and branch and does not silently amend an existing PR/MR.
30
- - Runtime context controls are supported only where exposed by the provider; unknown defaults remain unknown. Codex explicit-model validation depends on its local model catalog or an injected catalog.
31
- - Git hooks are disabled for the application-authored task commit; configure required checks as validation commands. Changed symlinks and submodules require manual handling.
32
- - The runner assumes exclusive checkout use. It detects boundary changes but does not sandbox arbitrary custom adapters or prevent every unrelated local tool write.
33
- - macOS/Linux process groups are required. Windows startup fails explicitly.
34
-
35
- Human review should concentrate on cancellation/ownership, recovery reconciliation and adapter permissions before enabling unattended work on a real repository. Completion means the automation has published its draft and review, not that the code is approved.