@kontourai/flow-agents 4.2.0 → 4.2.1
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/CHANGELOG.md +9 -0
- package/build/src/builder-flow-runtime.js +10 -5
- package/context/contracts/verification-contract.md +6 -0
- package/dist/base/build/package.json +1 -1
- package/dist/base/build/src/builder-flow-runtime.js +10 -5
- package/dist/base/context/contracts/verification-contract.md +6 -0
- package/dist/base/evals/acceptance/prove-capture-teeth.sh +24 -0
- package/dist/base/evals/integration/test_effective_backlog_settings.sh +118 -15
- package/dist/base/install.sh +1 -1
- package/dist/base/scripts/hooks/lib/codex-exit-code.js +75 -23
- package/dist/claude-code/build/package.json +1 -1
- package/dist/claude-code/build/src/builder-flow-runtime.js +10 -5
- package/dist/claude-code/context/contracts/verification-contract.md +6 -0
- package/dist/claude-code/evals/acceptance/prove-capture-teeth.sh +24 -0
- package/dist/claude-code/evals/integration/test_effective_backlog_settings.sh +118 -15
- package/dist/claude-code/install.sh +1 -1
- package/dist/claude-code/scripts/hooks/lib/codex-exit-code.js +75 -23
- package/dist/codex/build/package.json +1 -1
- package/dist/codex/build/src/builder-flow-runtime.js +10 -5
- package/dist/codex/context/contracts/verification-contract.md +6 -0
- package/dist/codex/evals/acceptance/prove-capture-teeth.sh +24 -0
- package/dist/codex/evals/integration/test_effective_backlog_settings.sh +118 -15
- package/dist/codex/install.sh +1 -1
- package/dist/codex/scripts/hooks/lib/codex-exit-code.js +75 -23
- package/dist/kiro/build/package.json +1 -1
- package/dist/kiro/build/src/builder-flow-runtime.js +10 -5
- package/dist/kiro/context/contracts/verification-contract.md +6 -0
- package/dist/kiro/evals/acceptance/prove-capture-teeth.sh +24 -0
- package/dist/kiro/evals/integration/test_effective_backlog_settings.sh +118 -15
- package/dist/kiro/install.sh +1 -1
- package/dist/kiro/scripts/hooks/lib/codex-exit-code.js +75 -23
- package/dist/opencode/build/package.json +1 -1
- package/dist/opencode/build/src/builder-flow-runtime.js +10 -5
- package/dist/opencode/context/contracts/verification-contract.md +6 -0
- package/dist/opencode/evals/acceptance/prove-capture-teeth.sh +24 -0
- package/dist/opencode/evals/integration/test_effective_backlog_settings.sh +118 -15
- package/dist/opencode/install.sh +1 -1
- package/dist/opencode/scripts/hooks/lib/codex-exit-code.js +75 -23
- package/dist/pi/build/package.json +1 -1
- package/dist/pi/build/src/builder-flow-runtime.js +10 -5
- package/dist/pi/context/contracts/verification-contract.md +6 -0
- package/dist/pi/evals/acceptance/prove-capture-teeth.sh +24 -0
- package/dist/pi/evals/integration/test_effective_backlog_settings.sh +118 -15
- package/dist/pi/install.sh +1 -1
- package/dist/pi/scripts/hooks/lib/codex-exit-code.js +75 -23
- package/evals/acceptance/prove-capture-teeth.sh +24 -0
- package/evals/integration/test_effective_backlog_settings.sh +118 -15
- package/package.json +2 -1
- package/scripts/hooks/lib/codex-exit-code.js +75 -23
- package/src/builder-flow-runtime.ts +10 -5
- package/src/cli/builder-flow-runtime.test.mjs +55 -2
- package/src/cli/codex-exit-code.test.mjs +217 -2
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
set -uo pipefail
|
|
3
3
|
|
|
4
4
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
CLI="$ROOT/build/src/cli.js"
|
|
6
|
+
BACKLOG_SETTINGS="$ROOT/context/settings/backlog-provider-settings.json"
|
|
7
|
+
ASSIGNMENT_SETTINGS="$ROOT/context/settings/assignment-provider-settings.json"
|
|
8
|
+
BACKLOG_GLOBAL="$ROOT/evals/fixtures/backlog-provider-settings/global-default.json"
|
|
9
|
+
BACKLOG_OVERRIDE="$ROOT/evals/fixtures/backlog-provider-settings/project-override.json"
|
|
10
|
+
TMPDIR_EVAL="$(mktemp -d)"
|
|
11
|
+
EMPTY_BACKLOG="$TMPDIR_EVAL/empty-backlog-settings.json"
|
|
12
|
+
EMPTY_ASSIGNMENT="$TMPDIR_EVAL/empty-assignment-settings.json"
|
|
13
|
+
trap 'rm -rf "$TMPDIR_EVAL"' EXIT
|
|
14
|
+
|
|
15
|
+
printf '{"schema_version":"1.0","projects":[]}\n' > "$EMPTY_BACKLOG"
|
|
16
|
+
printf '{"schema_version":"1.0","projects":[]}\n' > "$EMPTY_ASSIGNMENT"
|
|
12
17
|
|
|
13
18
|
errors=0
|
|
14
19
|
pass() { echo " ✓ $1"; }
|
|
@@ -18,17 +23,32 @@ json_value() {
|
|
|
18
23
|
NO_COLOR=1 FORCE_COLOR=0 node -e 'const fs=require("fs"); let cur=JSON.parse(fs.readFileSync(0,"utf8")); for (const part of process.argv[1].split(".")) cur=Array.isArray(cur) ? cur[Number(part)] : cur[part]; console.log(cur);' "$1"
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
echo "=== Effective
|
|
26
|
+
echo "=== Effective provider settings ==="
|
|
27
|
+
|
|
28
|
+
# Build exactly once before calling the actual compiled CLI commands below.
|
|
29
|
+
if (cd "$ROOT" && npm run build --silent); then
|
|
30
|
+
pass "compiled CLI build succeeds"
|
|
31
|
+
else
|
|
32
|
+
fail "compiled CLI build succeeds"
|
|
33
|
+
fi
|
|
22
34
|
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
if [[ ! -f "$CLI" ]]; then
|
|
36
|
+
fail "compiled CLI is available"
|
|
37
|
+
else
|
|
38
|
+
pass "compiled CLI is available"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
echo "--- existing backlog precedence and ask-user behavior ---"
|
|
42
|
+
|
|
43
|
+
default_result="$(node "$CLI" effective-backlog-settings --repo-path "$ROOT" --json 2>/dev/null)"
|
|
44
|
+
[[ "$(printf '%s' "$default_result" | json_value status)" == "configured" ]] && pass "default project settings resolve through compiled CLI" || fail "default project settings resolve through compiled CLI"
|
|
25
45
|
[[ "$(printf '%s' "$default_result" | json_value settings.work_item_provider.kind)" == "github" ]] && pass "default provider kind is github" || fail "default provider kind is github"
|
|
26
46
|
[[ "$(printf '%s' "$default_result" | json_value settings.work_item_provider.repo.owner)" == "kontourai" ]] && pass "default repo owner is kontourai" || fail "default repo owner is kontourai"
|
|
27
47
|
[[ "$(printf '%s' "$default_result" | json_value settings.work_item_provider.repo.name)" == "flow-agents" ]] && pass "default repo name is flow-agents" || fail "default repo name is flow-agents"
|
|
28
48
|
[[ "$(printf '%s' "$default_result" | json_value settings.board_provider.board.number)" == "1" ]] && pass "default project number is 1" || fail "default project number is 1"
|
|
29
49
|
! printf '%s' "$default_result" | rg -q '/build/context/settings/backlog-provider-settings.json' && pass "default settings path is not build-relative" || fail "default settings path is not build-relative"
|
|
30
50
|
|
|
31
|
-
configured="$(node "$
|
|
51
|
+
configured="$(node "$CLI" effective-backlog-settings --repo-path "$ROOT" --project-settings "$BACKLOG_SETTINGS" --global-settings "$EMPTY_BACKLOG" --json)"
|
|
32
52
|
[[ "$(printf '%s' "$configured" | json_value status)" == "configured" ]] && pass "configured repo resolves" || fail "configured repo resolves"
|
|
33
53
|
[[ "$(printf '%s' "$configured" | json_value settings.work_item_provider.kind)" == "github" ]] && pass "provider kind is github" || fail "provider kind is github"
|
|
34
54
|
[[ "$(printf '%s' "$configured" | json_value settings.work_item_provider.repo.owner)" == "kontourai" ]] && pass "repo owner is kontourai" || fail "repo owner is kontourai"
|
|
@@ -38,21 +58,104 @@ printf '%s' "$configured" | rg -F -q '"ready_statuses": [' && pass "filters incl
|
|
|
38
58
|
printf '%s' "$configured" | rg -F -q '"include_labels": []' && pass "filters do not require labels by default" || fail "filters do not require labels by default"
|
|
39
59
|
printf '%s' "$configured" | rg -q '"prefer_finishing_active_work": true' && pass "WIP policy prefers finishing active work" || fail "WIP policy prefers finishing active work"
|
|
40
60
|
|
|
41
|
-
override_result="$(node "$
|
|
61
|
+
override_result="$(node "$CLI" effective-backlog-settings --repo-path "$ROOT" --project-settings "$BACKLOG_OVERRIDE" --global-settings "$BACKLOG_GLOBAL" --json)"
|
|
42
62
|
[[ "$(printf '%s' "$override_result" | json_value source)" == "project" ]] && pass "project settings override global defaults" || fail "project settings override global defaults"
|
|
43
63
|
[[ "$(printf '%s' "$override_result" | json_value settings.board_provider.board.number)" == "1" ]] && pass "project override keeps Project 1" || fail "project override keeps Project 1"
|
|
44
64
|
! printf '%s' "$override_result" | rg -q 'global-ready' && pass "project override replaces global filters" || fail "project override replaces global filters"
|
|
45
65
|
|
|
46
|
-
missing_output="$(node "$
|
|
66
|
+
missing_output="$(node "$CLI" effective-backlog-settings --repo-path "$ROOT" --project-settings "$EMPTY_BACKLOG" --global-settings "$EMPTY_BACKLOG" --json 2>/dev/null)"
|
|
47
67
|
missing_status=$?
|
|
48
68
|
[[ "$missing_status" -eq 2 ]] && pass "missing settings exits with ask-user status" || fail "missing settings exits with ask-user status"
|
|
49
69
|
[[ "$(printf '%s' "$missing_output" | json_value status)" == "ask_user" ]] && pass "missing settings reports ask_user" || fail "missing settings reports ask_user"
|
|
50
70
|
printf '%s' "$missing_output" | rg -q 'WorkItemProvider and BoardProvider' && pass "missing settings names provider roles" || fail "missing settings names provider roles"
|
|
51
71
|
|
|
72
|
+
echo "--- public self-configuration and external multi-repository settings ---"
|
|
73
|
+
|
|
74
|
+
# Product-specific fleet routing belongs in consumer-owned settings. The public package proves
|
|
75
|
+
# that an external settings file can configure multiple repositories without embedding those
|
|
76
|
+
# consumer mappings in Flow Agents source.
|
|
77
|
+
if node --input-type=module - "$ROOT" "$CLI" "$TMPDIR_EVAL/repos" "$BACKLOG_SETTINGS" "$ASSIGNMENT_SETTINGS" "$EMPTY_BACKLOG" "$EMPTY_ASSIGNMENT" <<'NODE'
|
|
78
|
+
import { execFileSync } from "node:child_process";
|
|
79
|
+
import fs from "node:fs";
|
|
80
|
+
import path from "node:path";
|
|
81
|
+
import Ajv2020 from "ajv/dist/2020.js";
|
|
82
|
+
|
|
83
|
+
const [root, cli, reposRoot, publicBacklogPath, publicAssignmentPath, emptyBacklog, emptyAssignment] = process.argv.slice(2);
|
|
84
|
+
const readJson = (file) => JSON.parse(fs.readFileSync(file, "utf8"));
|
|
85
|
+
const assert = (condition, message) => {
|
|
86
|
+
if (!condition) throw new Error(message);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const backlogSchema = readJson(path.join(root, "schemas/backlog-provider-settings.schema.json"));
|
|
90
|
+
const assignmentSchema = readJson(path.join(root, "schemas/assignment-provider-settings.schema.json"));
|
|
91
|
+
const ajv = new Ajv2020({ allErrors: true });
|
|
92
|
+
const validateBacklog = ajv.compile(backlogSchema);
|
|
93
|
+
const validateAssignment = ajv.compile(assignmentSchema);
|
|
94
|
+
|
|
95
|
+
const publicBacklog = readJson(publicBacklogPath);
|
|
96
|
+
const publicAssignment = readJson(publicAssignmentPath);
|
|
97
|
+
assert(validateBacklog(publicBacklog), `public backlog settings schema validation failed: ${ajv.errorsText(validateBacklog.errors)}`);
|
|
98
|
+
assert(validateAssignment(publicAssignment), `public assignment settings schema validation failed: ${ajv.errorsText(validateAssignment.errors)}`);
|
|
99
|
+
assert(publicBacklog.projects.length === 1 && publicBacklog.projects[0]?.project?.repo?.name === "flow-agents", "public backlog settings must remain self-only");
|
|
100
|
+
assert(publicAssignment.projects.length === 1 && publicAssignment.projects[0]?.project?.repo?.name === "flow-agents", "public assignment settings must remain self-only");
|
|
101
|
+
|
|
102
|
+
const owner = "example-org";
|
|
103
|
+
const targets = ["alpha", "beta"];
|
|
104
|
+
const projectNumber = 42;
|
|
105
|
+
const repoRef = (name) => ({ owner, name, url: `https://github.com/${owner}/${name}` });
|
|
106
|
+
const externalBacklog = {
|
|
107
|
+
schema_version: "1.0",
|
|
108
|
+
projects: targets.map((name) => ({
|
|
109
|
+
project: { repo: repoRef(name) },
|
|
110
|
+
work_item_provider: { role: "WorkItemProvider", kind: "github", repo: repoRef(name), capabilities: ["issues", "labels", "assignees", "pr_links", "comments"] },
|
|
111
|
+
board_provider: { role: "BoardProvider", kind: "github", repo: repoRef(name), board: { type: "github_project", owner, number: projectNumber, url: `https://github.com/orgs/${owner}/projects/${projectNumber}` }, capabilities: ["projects_boards", "status_fields", "custom_fields"] },
|
|
112
|
+
selection: { filters: { issue_state: "open", include_labels: [], ready_statuses: ["ready"], exclude_statuses: ["triage", "in_progress", "blocked", "review", "verification", "done"] }, wip_policy: { prefer_finishing_active_work: true, active_statuses: ["in_progress", "review", "verification"], block_new_work_when_active_count_exceeds: 0 } },
|
|
113
|
+
})),
|
|
114
|
+
};
|
|
115
|
+
const externalAssignment = {
|
|
116
|
+
schema_version: "1.0",
|
|
117
|
+
projects: targets.map((name) => ({
|
|
118
|
+
project: { repo: repoRef(name) },
|
|
119
|
+
provider: { kind: "github", repo: repoRef(name), capabilities: ["assignees", "labels", "comments"] },
|
|
120
|
+
policy: { label_name: "agent:claimed", claim_comment_marker: "<!-- flow-agents:assignment-claim -->", human_assignee_policy: { behavior: "ask_first", idle_threshold_days: 3 }, comment_refresh_on_phase_transition: false },
|
|
121
|
+
})),
|
|
122
|
+
};
|
|
123
|
+
assert(validateBacklog(externalBacklog), `external backlog settings schema validation failed: ${ajv.errorsText(validateBacklog.errors)}`);
|
|
124
|
+
assert(validateAssignment(externalAssignment), `external assignment settings schema validation failed: ${ajv.errorsText(validateAssignment.errors)}`);
|
|
125
|
+
|
|
126
|
+
fs.mkdirSync(reposRoot, { recursive: true });
|
|
127
|
+
const externalBacklogPath = path.join(reposRoot, "backlog-provider-settings.json");
|
|
128
|
+
const externalAssignmentPath = path.join(reposRoot, "assignment-provider-settings.json");
|
|
129
|
+
fs.writeFileSync(externalBacklogPath, JSON.stringify(externalBacklog, null, 2));
|
|
130
|
+
fs.writeFileSync(externalAssignmentPath, JSON.stringify(externalAssignment, null, 2));
|
|
131
|
+
|
|
132
|
+
for (const name of targets) {
|
|
133
|
+
const repoPath = path.join(reposRoot, name);
|
|
134
|
+
fs.mkdirSync(repoPath, { recursive: true });
|
|
135
|
+
fs.writeFileSync(path.join(repoPath, "package.json"), JSON.stringify({ name: `fixture-${name}`, repository: { type: "git", url: `https://github.com/${owner}/${name}.git` } }, null, 2));
|
|
136
|
+
const resolve = (command, globalPath, emptyProject) => JSON.parse(execFileSync(process.execPath, [cli, command, "--repo-path", repoPath, "--project-settings", emptyProject, "--global-settings", globalPath, "--json"], { encoding: "utf8" }));
|
|
137
|
+
const backlogResult = resolve("effective-backlog-settings", externalBacklogPath, emptyBacklog);
|
|
138
|
+
const assignmentResult = resolve("effective-assignment-provider-settings", externalAssignmentPath, emptyAssignment);
|
|
139
|
+
|
|
140
|
+
assert(backlogResult.status === "configured" && backlogResult.source === "global", `${name} backlog settings must resolve from the external file`);
|
|
141
|
+
assert(backlogResult.settings.work_item_provider?.repo?.name === name, `${name} must select its own work item repository`);
|
|
142
|
+
assert(backlogResult.settings.board_provider?.board?.number === projectNumber, `${name} must select the external board`);
|
|
143
|
+
assert(assignmentResult.status === "configured" && assignmentResult.source === "global", `${name} assignment settings must resolve from the external file`);
|
|
144
|
+
assert(assignmentResult.settings.provider?.repo?.name === name, `${name} must select its own assignment repository`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.log("Public settings remain self-only and external multi-repository settings resolve through the supported global precedence layer.");
|
|
148
|
+
NODE
|
|
149
|
+
then
|
|
150
|
+
pass "public settings stay self-only and external multi-repository settings resolve"
|
|
151
|
+
else
|
|
152
|
+
fail "public settings stay self-only and external multi-repository settings resolve"
|
|
153
|
+
fi
|
|
154
|
+
|
|
52
155
|
if [[ "$errors" -eq 0 ]]; then
|
|
53
|
-
echo "Effective
|
|
156
|
+
echo "Effective provider settings checks passed"
|
|
54
157
|
else
|
|
55
|
-
echo "Effective
|
|
158
|
+
echo "Effective provider settings checks failed: $errors"
|
|
56
159
|
fi
|
|
57
160
|
|
|
58
161
|
exit "$errors"
|
package/dist/pi/install.sh
CHANGED
|
@@ -72,7 +72,7 @@ if [[ ! -e "$DEST/AGENTS.md" && ! -L "$DEST/AGENTS.md" ]]; then
|
|
|
72
72
|
rm -f "$instruction_tmp"
|
|
73
73
|
fi
|
|
74
74
|
if command -v node >/dev/null 2>&1; then
|
|
75
|
-
node "$DEST/scripts/install-merge.js" --stamp-only --version "4.2.
|
|
75
|
+
node "$DEST/scripts/install-merge.js" --stamp-only --version "4.2.1" --install-record "$DEST/.flow-agents/install.json" --runtime "pi" || true
|
|
76
76
|
fi
|
|
77
77
|
if [[ ${#CONSOLE_CONFIG_ARGS[@]} -gt 0 || -n "${FLOW_AGENTS_TELEMETRY_SINK:-}" || -n "${FLOW_AGENTS_TELEMETRY_SINKS:-}" || -n "${FLOW_AGENTS_CONSOLE_URL:-}" || -n "${CONSOLE_TELEMETRY_URL:-}" || -n "${CONSOLE_URL:-}" || -n "${FLOW_AGENTS_CONSOLE_TOKEN_FILE:-}" || -n "${CONSOLE_TELEMETRY_TOKEN_FILE:-}" ]]; then
|
|
78
78
|
bash "$DEST/scripts/telemetry/install-console-config.sh" "$DEST/scripts/telemetry/telemetry.conf" "${CONSOLE_CONFIG_ARGS[@]}"
|
|
@@ -113,10 +113,13 @@ function normalizeCallArguments(argumentsField) {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
if (!parsed || typeof parsed !== 'object') return null;
|
|
116
|
-
|
|
116
|
+
// Codex records exec_command arguments under `cmd`; older shell fixtures
|
|
117
|
+
// and some adapters use `command`.
|
|
118
|
+
return normalizeCommandText(parsed.cmd) || normalizeCommandText(parsed.command);
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
const OUTPUT_FIELD_NEEDLE = '"output":"';
|
|
122
|
+
const COMMAND_FUNCTION_NAMES = new Set(['exec_command', 'shell']);
|
|
120
123
|
|
|
121
124
|
/**
|
|
122
125
|
* parseCandidateLine(line, maxLineHeadBytes) → candidate | null
|
|
@@ -149,7 +152,15 @@ function parseCandidateLine(line, maxLineHeadBytes) {
|
|
|
149
152
|
return { type: 'function_call_output', callId, output: payload.output };
|
|
150
153
|
}
|
|
151
154
|
if (payload.type === 'function_call') {
|
|
152
|
-
|
|
155
|
+
const name = typeof payload.name === 'string' && payload.name.trim() ? payload.name.trim() : null;
|
|
156
|
+
const command = normalizeCallArguments(payload.arguments);
|
|
157
|
+
const commandCapable = name === null || COMMAND_FUNCTION_NAMES.has(name) || command !== null;
|
|
158
|
+
return {
|
|
159
|
+
type: 'function_call',
|
|
160
|
+
callId,
|
|
161
|
+
commandCapable,
|
|
162
|
+
command,
|
|
163
|
+
};
|
|
153
164
|
}
|
|
154
165
|
return null;
|
|
155
166
|
}
|
|
@@ -214,13 +225,12 @@ function resolveContainedRealPath(transcriptPath) {
|
|
|
214
225
|
* Correlation policy (Decision B, #470 iteration 2, HIGH finding #4), in
|
|
215
226
|
* priority order:
|
|
216
227
|
* 1. call_id match wins — authoritative.
|
|
217
|
-
* 2. Absent a call_id match:
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
* back to the newest `function_call_output` banner.
|
|
228
|
+
* 2. Absent a call_id match: pair outputs with calls by rollout call_id and
|
|
229
|
+
* select the only pair whose normalized arguments match `command`. Zero
|
|
230
|
+
* matches or multiple matches DECLINE (`null`) rather than attribute a
|
|
231
|
+
* neighboring or repeated command's exit code.
|
|
232
|
+
* 3. If no pairing is resolvable at all and the rollout contains exactly one
|
|
233
|
+
* output with no function call, use that genuinely unpaired legacy output.
|
|
224
234
|
*
|
|
225
235
|
* Any failure (missing/unreadable/non-regular file, containment violation,
|
|
226
236
|
* malformed JSON lines, no candidate found) yields `null` — never throws.
|
|
@@ -271,41 +281,83 @@ function readExitCodeFromRollout(transcriptPath, options) {
|
|
|
271
281
|
}
|
|
272
282
|
if (truncated && lines.length > 1) lines.shift();
|
|
273
283
|
|
|
274
|
-
|
|
284
|
+
const outputsByRequestedCallId = [];
|
|
275
285
|
let newestOutputEntry = null;
|
|
276
|
-
|
|
286
|
+
const outputEntries = [];
|
|
287
|
+
const commandByCallId = new Map();
|
|
288
|
+
const seenFunctionCallIds = new Set();
|
|
289
|
+
const ambiguousCallIds = new Set();
|
|
290
|
+
let functionCallCount = 0;
|
|
291
|
+
let sawUnclassifiableLine = false;
|
|
292
|
+
let sawUnresolvableFunctionCall = false;
|
|
277
293
|
|
|
278
294
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
279
295
|
const line = lines[i].trim();
|
|
280
296
|
if (!line) continue;
|
|
281
297
|
const candidate = parseCandidateLine(line, maxLineHeadBytes);
|
|
282
|
-
if (!candidate)
|
|
298
|
+
if (!candidate) {
|
|
299
|
+
if (Buffer.byteLength(line, 'utf8') > maxLineHeadBytes) {
|
|
300
|
+
sawUnclassifiableLine = true;
|
|
301
|
+
} else {
|
|
302
|
+
try {
|
|
303
|
+
JSON.parse(line); // valid non-candidate events do not poison correlation
|
|
304
|
+
} catch {
|
|
305
|
+
sawUnclassifiableLine = true;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
continue; // malformed/partial/unrecognized line — skip, keep scanning
|
|
309
|
+
}
|
|
283
310
|
|
|
284
311
|
if (candidate.type === 'function_call_output') {
|
|
312
|
+
outputEntries.push(candidate); // scan order is newest to oldest
|
|
285
313
|
if (newestOutputEntry === null) newestOutputEntry = candidate; // first seen scanning backward = newest
|
|
286
314
|
if (callId && candidate.callId === callId) {
|
|
287
|
-
|
|
288
|
-
break; // call_id match is authoritative — stop scanning
|
|
315
|
+
outputsByRequestedCallId.push(candidate);
|
|
289
316
|
}
|
|
290
317
|
} else if (candidate.type === 'function_call') {
|
|
291
|
-
|
|
292
|
-
|
|
318
|
+
functionCallCount += 1;
|
|
319
|
+
if (candidate.callId) {
|
|
320
|
+
if (seenFunctionCallIds.has(candidate.callId)) ambiguousCallIds.add(candidate.callId);
|
|
321
|
+
else seenFunctionCallIds.add(candidate.callId);
|
|
322
|
+
}
|
|
323
|
+
if (candidate.commandCapable && candidate.callId && candidate.command !== null) {
|
|
324
|
+
if (!commandByCallId.has(candidate.callId)) commandByCallId.set(candidate.callId, candidate.command);
|
|
325
|
+
} else if (candidate.commandCapable) {
|
|
326
|
+
sawUnresolvableFunctionCall = true;
|
|
293
327
|
}
|
|
294
328
|
}
|
|
295
329
|
}
|
|
296
330
|
|
|
297
331
|
let chosenOutput = null;
|
|
298
|
-
if (
|
|
299
|
-
|
|
332
|
+
if ((callId || command) && sawUnclassifiableLine) return null;
|
|
333
|
+
if (callId) {
|
|
334
|
+
if (outputsByRequestedCallId.length === 1 && !ambiguousCallIds.has(callId)) {
|
|
335
|
+
chosenOutput = outputsByRequestedCallId[0].output;
|
|
336
|
+
} else {
|
|
337
|
+
return null; // explicit call ID is unresolved, reused, or has duplicate outputs
|
|
338
|
+
}
|
|
300
339
|
} else if (newestOutputEntry) {
|
|
301
|
-
if (
|
|
302
|
-
if (
|
|
303
|
-
|
|
340
|
+
if (command) {
|
|
341
|
+
if (sawUnresolvableFunctionCall) return null;
|
|
342
|
+
const matchingCallIds = [...commandByCallId.entries()]
|
|
343
|
+
.filter(([, candidateCommand]) => candidateCommand === command)
|
|
344
|
+
.map(([candidateCallId]) => candidateCallId);
|
|
345
|
+
const matches = outputEntries.filter((entry) => commandByCallId.get(entry.callId) === command);
|
|
346
|
+
if (matchingCallIds.length === 1
|
|
347
|
+
&& !ambiguousCallIds.has(matchingCallIds[0])
|
|
348
|
+
&& matches.length === 1) {
|
|
349
|
+
chosenOutput = matches[0].output;
|
|
350
|
+
} else if (matchingCallIds.length > 0 || matches.length > 0 || commandByCallId.size > 0) {
|
|
351
|
+
return null; // no unique command correlation
|
|
352
|
+
} else if (!truncated && outputEntries.length === 1 && functionCallCount === 0) {
|
|
353
|
+
chosenOutput = newestOutputEntry.output; // genuinely unpaired legacy fallback
|
|
304
354
|
} else {
|
|
305
|
-
return null; //
|
|
355
|
+
return null; // multi-call or partially paired tail
|
|
306
356
|
}
|
|
357
|
+
} else if (!truncated && outputEntries.length === 1 && functionCallCount === 0) {
|
|
358
|
+
chosenOutput = newestOutputEntry.output; // genuinely unpaired legacy fallback
|
|
307
359
|
} else {
|
|
308
|
-
|
|
360
|
+
return null; // no correlation signal for a paired or multi-call tail
|
|
309
361
|
}
|
|
310
362
|
}
|
|
311
363
|
|
|
@@ -246,6 +246,30 @@ codex_banner_teeth(){
|
|
|
246
246
|
else
|
|
247
247
|
_f "Codex banner CALL_ID: did not record pass/exitCode:0 for the call_id-matched entry: $(cat "$log_callid" 2>/dev/null)"
|
|
248
248
|
fi
|
|
249
|
+
|
|
250
|
+
# --- PARALLEL command correlation (#644): current Codex exec_command calls
|
|
251
|
+
# store arguments under `cmd`. With no hook call_id, each PostToolUse event
|
|
252
|
+
# must resolve its exact function_call/output pair by normalized command.
|
|
253
|
+
local proj_parallel; proj_parallel="$(mktemp -d)"
|
|
254
|
+
mkdir -p "$proj_parallel/.kontourai/flow-agents/cap-codex-parallel"
|
|
255
|
+
printf '# Repo\n' > "$proj_parallel/AGENTS.md"
|
|
256
|
+
printf '%s' '{"schema_version":"1.0","task_slug":"cap-codex-parallel","status":"in_progress","phase":"verification","updated_at":"2026-06-23T00:00:00Z"}' > "$proj_parallel/.kontourai/flow-agents/cap-codex-parallel/state.json"
|
|
257
|
+
local rollout_parallel="$proj_parallel/rollout-parallel.jsonl"
|
|
258
|
+
printf '%s\n' '{"timestamp":"2026-06-23T00:00:00Z","type":"response_item","payload":{"type":"function_call","call_id":"call_pass","name":"exec_command","arguments":"{\"cmd\":\"find seed -type f\"}"}}' > "$rollout_parallel"
|
|
259
|
+
printf '%s\n' '{"timestamp":"2026-06-23T00:00:01Z","type":"response_item","payload":{"type":"function_call","call_id":"call_fail","name":"exec_command","arguments":"{\"cmd\":\"cat seed/src/window.js\"}"}}' >> "$rollout_parallel"
|
|
260
|
+
printf '%s\n' '{"timestamp":"2026-06-23T00:00:02Z","type":"response_item","payload":{"type":"function_call_output","call_id":"call_pass","output":"Process exited with code 0\nOutput:\n..."}}' >> "$rollout_parallel"
|
|
261
|
+
printf '%s\n' '{"timestamp":"2026-06-23T00:00:03Z","type":"response_item","payload":{"type":"function_call_output","call_id":"call_fail","output":"Process exited with code 1\nOutput:\n..."}}' >> "$rollout_parallel"
|
|
262
|
+
printf '{"hook_event_name":"PostToolUse","tool_name":"Bash","cwd":"%s","tool_input":{"command":"find seed -type f"},"transcript_path":"%s"}' "$proj_parallel" "$rollout_parallel" \
|
|
263
|
+
| env CODEX_HOME="$home" CLAUDE_PROJECT_DIR="$home" bash -c "$capcmd" >/dev/null 2>&1 || true
|
|
264
|
+
printf '{"hook_event_name":"PostToolUse","tool_name":"Bash","cwd":"%s","tool_input":{"command":"cat seed/src/window.js"},"transcript_path":"%s"}' "$proj_parallel" "$rollout_parallel" \
|
|
265
|
+
| env CODEX_HOME="$home" CLAUDE_PROJECT_DIR="$home" bash -c "$capcmd" >/dev/null 2>&1 || true
|
|
266
|
+
local log_parallel="$proj_parallel/.kontourai/flow-agents/cap-codex-parallel/command-log.jsonl"
|
|
267
|
+
if rg -q '"command":"find seed -type f","observedResult":"pass","exitCode":0' "$log_parallel" 2>/dev/null \
|
|
268
|
+
&& rg -q '"command":"cat seed/src/window.js","observedResult":"fail","exitCode":1' "$log_parallel" 2>/dev/null; then
|
|
269
|
+
_p "Codex banner PARALLEL: cmd correlation keeps each command attached to its own exit code"
|
|
270
|
+
else
|
|
271
|
+
_f "Codex banner PARALLEL: command/result attribution crossed or stayed unresolved: $(cat "$log_parallel" 2>/dev/null)"
|
|
272
|
+
fi
|
|
249
273
|
}
|
|
250
274
|
|
|
251
275
|
run_bundle "Claude Code" "$ROOT/dist/claude-code/install.sh" ".claude/settings.json" "CLAUDE_PROJECT_DIR"
|
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
set -uo pipefail
|
|
3
3
|
|
|
4
4
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
CLI="$ROOT/build/src/cli.js"
|
|
6
|
+
BACKLOG_SETTINGS="$ROOT/context/settings/backlog-provider-settings.json"
|
|
7
|
+
ASSIGNMENT_SETTINGS="$ROOT/context/settings/assignment-provider-settings.json"
|
|
8
|
+
BACKLOG_GLOBAL="$ROOT/evals/fixtures/backlog-provider-settings/global-default.json"
|
|
9
|
+
BACKLOG_OVERRIDE="$ROOT/evals/fixtures/backlog-provider-settings/project-override.json"
|
|
10
|
+
TMPDIR_EVAL="$(mktemp -d)"
|
|
11
|
+
EMPTY_BACKLOG="$TMPDIR_EVAL/empty-backlog-settings.json"
|
|
12
|
+
EMPTY_ASSIGNMENT="$TMPDIR_EVAL/empty-assignment-settings.json"
|
|
13
|
+
trap 'rm -rf "$TMPDIR_EVAL"' EXIT
|
|
14
|
+
|
|
15
|
+
printf '{"schema_version":"1.0","projects":[]}\n' > "$EMPTY_BACKLOG"
|
|
16
|
+
printf '{"schema_version":"1.0","projects":[]}\n' > "$EMPTY_ASSIGNMENT"
|
|
12
17
|
|
|
13
18
|
errors=0
|
|
14
19
|
pass() { echo " ✓ $1"; }
|
|
@@ -18,17 +23,32 @@ json_value() {
|
|
|
18
23
|
NO_COLOR=1 FORCE_COLOR=0 node -e 'const fs=require("fs"); let cur=JSON.parse(fs.readFileSync(0,"utf8")); for (const part of process.argv[1].split(".")) cur=Array.isArray(cur) ? cur[Number(part)] : cur[part]; console.log(cur);' "$1"
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
echo "=== Effective
|
|
26
|
+
echo "=== Effective provider settings ==="
|
|
27
|
+
|
|
28
|
+
# Build exactly once before calling the actual compiled CLI commands below.
|
|
29
|
+
if (cd "$ROOT" && npm run build --silent); then
|
|
30
|
+
pass "compiled CLI build succeeds"
|
|
31
|
+
else
|
|
32
|
+
fail "compiled CLI build succeeds"
|
|
33
|
+
fi
|
|
22
34
|
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
if [[ ! -f "$CLI" ]]; then
|
|
36
|
+
fail "compiled CLI is available"
|
|
37
|
+
else
|
|
38
|
+
pass "compiled CLI is available"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
echo "--- existing backlog precedence and ask-user behavior ---"
|
|
42
|
+
|
|
43
|
+
default_result="$(node "$CLI" effective-backlog-settings --repo-path "$ROOT" --json 2>/dev/null)"
|
|
44
|
+
[[ "$(printf '%s' "$default_result" | json_value status)" == "configured" ]] && pass "default project settings resolve through compiled CLI" || fail "default project settings resolve through compiled CLI"
|
|
25
45
|
[[ "$(printf '%s' "$default_result" | json_value settings.work_item_provider.kind)" == "github" ]] && pass "default provider kind is github" || fail "default provider kind is github"
|
|
26
46
|
[[ "$(printf '%s' "$default_result" | json_value settings.work_item_provider.repo.owner)" == "kontourai" ]] && pass "default repo owner is kontourai" || fail "default repo owner is kontourai"
|
|
27
47
|
[[ "$(printf '%s' "$default_result" | json_value settings.work_item_provider.repo.name)" == "flow-agents" ]] && pass "default repo name is flow-agents" || fail "default repo name is flow-agents"
|
|
28
48
|
[[ "$(printf '%s' "$default_result" | json_value settings.board_provider.board.number)" == "1" ]] && pass "default project number is 1" || fail "default project number is 1"
|
|
29
49
|
! printf '%s' "$default_result" | rg -q '/build/context/settings/backlog-provider-settings.json' && pass "default settings path is not build-relative" || fail "default settings path is not build-relative"
|
|
30
50
|
|
|
31
|
-
configured="$(node "$
|
|
51
|
+
configured="$(node "$CLI" effective-backlog-settings --repo-path "$ROOT" --project-settings "$BACKLOG_SETTINGS" --global-settings "$EMPTY_BACKLOG" --json)"
|
|
32
52
|
[[ "$(printf '%s' "$configured" | json_value status)" == "configured" ]] && pass "configured repo resolves" || fail "configured repo resolves"
|
|
33
53
|
[[ "$(printf '%s' "$configured" | json_value settings.work_item_provider.kind)" == "github" ]] && pass "provider kind is github" || fail "provider kind is github"
|
|
34
54
|
[[ "$(printf '%s' "$configured" | json_value settings.work_item_provider.repo.owner)" == "kontourai" ]] && pass "repo owner is kontourai" || fail "repo owner is kontourai"
|
|
@@ -38,21 +58,104 @@ printf '%s' "$configured" | rg -F -q '"ready_statuses": [' && pass "filters incl
|
|
|
38
58
|
printf '%s' "$configured" | rg -F -q '"include_labels": []' && pass "filters do not require labels by default" || fail "filters do not require labels by default"
|
|
39
59
|
printf '%s' "$configured" | rg -q '"prefer_finishing_active_work": true' && pass "WIP policy prefers finishing active work" || fail "WIP policy prefers finishing active work"
|
|
40
60
|
|
|
41
|
-
override_result="$(node "$
|
|
61
|
+
override_result="$(node "$CLI" effective-backlog-settings --repo-path "$ROOT" --project-settings "$BACKLOG_OVERRIDE" --global-settings "$BACKLOG_GLOBAL" --json)"
|
|
42
62
|
[[ "$(printf '%s' "$override_result" | json_value source)" == "project" ]] && pass "project settings override global defaults" || fail "project settings override global defaults"
|
|
43
63
|
[[ "$(printf '%s' "$override_result" | json_value settings.board_provider.board.number)" == "1" ]] && pass "project override keeps Project 1" || fail "project override keeps Project 1"
|
|
44
64
|
! printf '%s' "$override_result" | rg -q 'global-ready' && pass "project override replaces global filters" || fail "project override replaces global filters"
|
|
45
65
|
|
|
46
|
-
missing_output="$(node "$
|
|
66
|
+
missing_output="$(node "$CLI" effective-backlog-settings --repo-path "$ROOT" --project-settings "$EMPTY_BACKLOG" --global-settings "$EMPTY_BACKLOG" --json 2>/dev/null)"
|
|
47
67
|
missing_status=$?
|
|
48
68
|
[[ "$missing_status" -eq 2 ]] && pass "missing settings exits with ask-user status" || fail "missing settings exits with ask-user status"
|
|
49
69
|
[[ "$(printf '%s' "$missing_output" | json_value status)" == "ask_user" ]] && pass "missing settings reports ask_user" || fail "missing settings reports ask_user"
|
|
50
70
|
printf '%s' "$missing_output" | rg -q 'WorkItemProvider and BoardProvider' && pass "missing settings names provider roles" || fail "missing settings names provider roles"
|
|
51
71
|
|
|
72
|
+
echo "--- public self-configuration and external multi-repository settings ---"
|
|
73
|
+
|
|
74
|
+
# Product-specific fleet routing belongs in consumer-owned settings. The public package proves
|
|
75
|
+
# that an external settings file can configure multiple repositories without embedding those
|
|
76
|
+
# consumer mappings in Flow Agents source.
|
|
77
|
+
if node --input-type=module - "$ROOT" "$CLI" "$TMPDIR_EVAL/repos" "$BACKLOG_SETTINGS" "$ASSIGNMENT_SETTINGS" "$EMPTY_BACKLOG" "$EMPTY_ASSIGNMENT" <<'NODE'
|
|
78
|
+
import { execFileSync } from "node:child_process";
|
|
79
|
+
import fs from "node:fs";
|
|
80
|
+
import path from "node:path";
|
|
81
|
+
import Ajv2020 from "ajv/dist/2020.js";
|
|
82
|
+
|
|
83
|
+
const [root, cli, reposRoot, publicBacklogPath, publicAssignmentPath, emptyBacklog, emptyAssignment] = process.argv.slice(2);
|
|
84
|
+
const readJson = (file) => JSON.parse(fs.readFileSync(file, "utf8"));
|
|
85
|
+
const assert = (condition, message) => {
|
|
86
|
+
if (!condition) throw new Error(message);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const backlogSchema = readJson(path.join(root, "schemas/backlog-provider-settings.schema.json"));
|
|
90
|
+
const assignmentSchema = readJson(path.join(root, "schemas/assignment-provider-settings.schema.json"));
|
|
91
|
+
const ajv = new Ajv2020({ allErrors: true });
|
|
92
|
+
const validateBacklog = ajv.compile(backlogSchema);
|
|
93
|
+
const validateAssignment = ajv.compile(assignmentSchema);
|
|
94
|
+
|
|
95
|
+
const publicBacklog = readJson(publicBacklogPath);
|
|
96
|
+
const publicAssignment = readJson(publicAssignmentPath);
|
|
97
|
+
assert(validateBacklog(publicBacklog), `public backlog settings schema validation failed: ${ajv.errorsText(validateBacklog.errors)}`);
|
|
98
|
+
assert(validateAssignment(publicAssignment), `public assignment settings schema validation failed: ${ajv.errorsText(validateAssignment.errors)}`);
|
|
99
|
+
assert(publicBacklog.projects.length === 1 && publicBacklog.projects[0]?.project?.repo?.name === "flow-agents", "public backlog settings must remain self-only");
|
|
100
|
+
assert(publicAssignment.projects.length === 1 && publicAssignment.projects[0]?.project?.repo?.name === "flow-agents", "public assignment settings must remain self-only");
|
|
101
|
+
|
|
102
|
+
const owner = "example-org";
|
|
103
|
+
const targets = ["alpha", "beta"];
|
|
104
|
+
const projectNumber = 42;
|
|
105
|
+
const repoRef = (name) => ({ owner, name, url: `https://github.com/${owner}/${name}` });
|
|
106
|
+
const externalBacklog = {
|
|
107
|
+
schema_version: "1.0",
|
|
108
|
+
projects: targets.map((name) => ({
|
|
109
|
+
project: { repo: repoRef(name) },
|
|
110
|
+
work_item_provider: { role: "WorkItemProvider", kind: "github", repo: repoRef(name), capabilities: ["issues", "labels", "assignees", "pr_links", "comments"] },
|
|
111
|
+
board_provider: { role: "BoardProvider", kind: "github", repo: repoRef(name), board: { type: "github_project", owner, number: projectNumber, url: `https://github.com/orgs/${owner}/projects/${projectNumber}` }, capabilities: ["projects_boards", "status_fields", "custom_fields"] },
|
|
112
|
+
selection: { filters: { issue_state: "open", include_labels: [], ready_statuses: ["ready"], exclude_statuses: ["triage", "in_progress", "blocked", "review", "verification", "done"] }, wip_policy: { prefer_finishing_active_work: true, active_statuses: ["in_progress", "review", "verification"], block_new_work_when_active_count_exceeds: 0 } },
|
|
113
|
+
})),
|
|
114
|
+
};
|
|
115
|
+
const externalAssignment = {
|
|
116
|
+
schema_version: "1.0",
|
|
117
|
+
projects: targets.map((name) => ({
|
|
118
|
+
project: { repo: repoRef(name) },
|
|
119
|
+
provider: { kind: "github", repo: repoRef(name), capabilities: ["assignees", "labels", "comments"] },
|
|
120
|
+
policy: { label_name: "agent:claimed", claim_comment_marker: "<!-- flow-agents:assignment-claim -->", human_assignee_policy: { behavior: "ask_first", idle_threshold_days: 3 }, comment_refresh_on_phase_transition: false },
|
|
121
|
+
})),
|
|
122
|
+
};
|
|
123
|
+
assert(validateBacklog(externalBacklog), `external backlog settings schema validation failed: ${ajv.errorsText(validateBacklog.errors)}`);
|
|
124
|
+
assert(validateAssignment(externalAssignment), `external assignment settings schema validation failed: ${ajv.errorsText(validateAssignment.errors)}`);
|
|
125
|
+
|
|
126
|
+
fs.mkdirSync(reposRoot, { recursive: true });
|
|
127
|
+
const externalBacklogPath = path.join(reposRoot, "backlog-provider-settings.json");
|
|
128
|
+
const externalAssignmentPath = path.join(reposRoot, "assignment-provider-settings.json");
|
|
129
|
+
fs.writeFileSync(externalBacklogPath, JSON.stringify(externalBacklog, null, 2));
|
|
130
|
+
fs.writeFileSync(externalAssignmentPath, JSON.stringify(externalAssignment, null, 2));
|
|
131
|
+
|
|
132
|
+
for (const name of targets) {
|
|
133
|
+
const repoPath = path.join(reposRoot, name);
|
|
134
|
+
fs.mkdirSync(repoPath, { recursive: true });
|
|
135
|
+
fs.writeFileSync(path.join(repoPath, "package.json"), JSON.stringify({ name: `fixture-${name}`, repository: { type: "git", url: `https://github.com/${owner}/${name}.git` } }, null, 2));
|
|
136
|
+
const resolve = (command, globalPath, emptyProject) => JSON.parse(execFileSync(process.execPath, [cli, command, "--repo-path", repoPath, "--project-settings", emptyProject, "--global-settings", globalPath, "--json"], { encoding: "utf8" }));
|
|
137
|
+
const backlogResult = resolve("effective-backlog-settings", externalBacklogPath, emptyBacklog);
|
|
138
|
+
const assignmentResult = resolve("effective-assignment-provider-settings", externalAssignmentPath, emptyAssignment);
|
|
139
|
+
|
|
140
|
+
assert(backlogResult.status === "configured" && backlogResult.source === "global", `${name} backlog settings must resolve from the external file`);
|
|
141
|
+
assert(backlogResult.settings.work_item_provider?.repo?.name === name, `${name} must select its own work item repository`);
|
|
142
|
+
assert(backlogResult.settings.board_provider?.board?.number === projectNumber, `${name} must select the external board`);
|
|
143
|
+
assert(assignmentResult.status === "configured" && assignmentResult.source === "global", `${name} assignment settings must resolve from the external file`);
|
|
144
|
+
assert(assignmentResult.settings.provider?.repo?.name === name, `${name} must select its own assignment repository`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.log("Public settings remain self-only and external multi-repository settings resolve through the supported global precedence layer.");
|
|
148
|
+
NODE
|
|
149
|
+
then
|
|
150
|
+
pass "public settings stay self-only and external multi-repository settings resolve"
|
|
151
|
+
else
|
|
152
|
+
fail "public settings stay self-only and external multi-repository settings resolve"
|
|
153
|
+
fi
|
|
154
|
+
|
|
52
155
|
if [[ "$errors" -eq 0 ]]; then
|
|
53
|
-
echo "Effective
|
|
156
|
+
echo "Effective provider settings checks passed"
|
|
54
157
|
else
|
|
55
|
-
echo "Effective
|
|
158
|
+
echo "Effective provider settings checks failed: $errors"
|
|
56
159
|
fi
|
|
57
160
|
|
|
58
161
|
exit "$errors"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kontourai/flow-agents",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.1",
|
|
4
4
|
"description": "Flow Agents — a Kontour product that applies Flow and Veritas discipline as a portable process layer inside the agent tools you already use: Claude Code, Codex, Kiro, opencode, pi, and GitHub Actions — with framework adapters (AWS Strands preview) on the same policy-engine contract.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -152,6 +152,7 @@
|
|
|
152
152
|
},
|
|
153
153
|
"devDependencies": {
|
|
154
154
|
"@types/node": "^26.1.1",
|
|
155
|
+
"gcp-metadata": "7.0.1",
|
|
155
156
|
"promptfoo": "^0.121.18",
|
|
156
157
|
"typescript": "^7.0.2"
|
|
157
158
|
},
|