@orchestrator-claude/definitions 3.37.0 → 3.37.2
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/agents/adversarial-reviewer.md +6 -1
- package/agents/api-extractor.md +6 -0
- package/agents/azure-devops-engineer.md +1 -2
- package/agents/business-rule-miner.md +6 -0
- package/agents/cicd-platform-engineer.md +1 -2
- package/agents/ciclo-engineer.md +50 -21
- package/agents/code-archaeologist.md +6 -0
- package/agents/debug-sidecar.md +6 -0
- package/agents/doc-sidecar.md +6 -0
- package/agents/docs-guardian.md +9 -3
- package/agents/frontend-engineer.md +1 -2
- package/agents/{ai-architect.md → harness-architect.md} +8 -3
- package/agents/implementer.md +6 -1
- package/agents/legacy-discoverer.md +6 -0
- package/agents/legacy-synthesizer.md +6 -0
- package/agents/methodology-researcher.md +6 -1
- package/agents/operational-artifact-builder.md +6 -1
- package/agents/orchestrator.md +27 -87
- package/agents/planner.md +6 -1
- package/agents/presentation-engineer.md +29 -15
- package/agents/python-data-engineer.md +0 -1
- package/agents/researcher.md +6 -0
- package/agents/reviewer.md +6 -1
- package/agents/schema-extractor.md +6 -0
- package/agents/software-architect.md +10 -5
- package/agents/specifier.md +6 -1
- package/agents/task-generator.md +6 -1
- package/agents/test-sidecar.md +6 -0
- package/dist/scripts/generate-hooks-matcher.d.ts +106 -0
- package/dist/scripts/generate-hooks-matcher.d.ts.map +1 -0
- package/dist/scripts/generate-hooks-matcher.js +153 -0
- package/dist/scripts/generate-hooks-matcher.js.map +1 -0
- package/dist/scripts/generate-manifest.d.ts +22 -0
- package/dist/scripts/generate-manifest.d.ts.map +1 -1
- package/dist/scripts/generate-manifest.js +67 -2
- package/dist/scripts/generate-manifest.js.map +1 -1
- package/hooks/agent-git-guard.mjs +63 -11
- package/hooks/dangling-guard.mjs +42 -12
- package/hooks/gate-guardian.mjs +38 -8
- package/hooks/hooks.json +3 -9
- package/hooks/lib/api-client.ts +128 -9
- package/hooks/lib/capability-scanner.ts +462 -55
- package/hooks/lib/dispatch-marker.ts +214 -0
- package/hooks/lib/git-checkpoint.ts +34 -37
- package/hooks/lib/observations-helper.ts +14 -1
- package/hooks/lib/security-gate-core.ts +271 -0
- package/hooks/mailbox-listener.mjs +69 -17
- package/hooks/playwright-output-guard.mjs +40 -10
- package/hooks/post-implement.mjs +38 -8
- package/hooks/pre-agent-dispatch.mjs +137 -16
- package/hooks/pre-agent-dispatch.ts +82 -15
- package/hooks/security-gate-honest.mjs +302 -0
- package/hooks/security-gate-honest.ts +148 -0
- package/hooks/session-start.mjs +304 -106
- package/hooks/session-start.ts +317 -71
- package/hooks/subagent-start.mjs +7673 -22
- package/hooks/subagent-start.ts +367 -16
- package/hooks/subagent-stop.mjs +102 -62
- package/hooks/subagent-stop.ts +124 -28
- package/hooks/user-prompt.mjs +43 -13
- package/hooks/verification-before-completion.mjs +63 -11
- package/hooks/workflow-guard.mjs +45 -11
- package/hooks-settings-template.json +1 -7
- package/manifest.json +77 -61
- package/package.json +4 -2
- package/skills/checkpoint-protocol/SKILL.md +19 -4
- package/skills/ciclo-lane-dispatch/SKILL.md +117 -8
- package/skills/fase-1b-projecao/SKILL.md +150 -0
- package/skills/lane-1-entrevista/SKILL.md +173 -0
- package/skills/orchestrator/SKILL.md +11 -3
- package/skills/workflow-coordination/SKILL.md +3 -3
- package/hooks/post-compact.mjs +0 -7931
- package/hooks/post-compact.ts +0 -170
|
@@ -6,28 +6,80 @@ import {
|
|
|
6
6
|
readFileSync as readFileSync2,
|
|
7
7
|
unlinkSync as unlinkSync2,
|
|
8
8
|
renameSync,
|
|
9
|
-
existsSync,
|
|
9
|
+
existsSync as existsSync2,
|
|
10
10
|
mkdirSync as mkdirSync2,
|
|
11
11
|
writeFileSync as writeFileSync2
|
|
12
12
|
} from "node:fs";
|
|
13
|
-
import { join as
|
|
13
|
+
import { join as join3 } from "node:path";
|
|
14
14
|
import { randomUUID } from "node:crypto";
|
|
15
15
|
|
|
16
16
|
// hooks/lib/api-client.ts
|
|
17
17
|
import { readFileSync, writeFileSync, mkdirSync, statSync, unlinkSync, readdirSync } from "node:fs";
|
|
18
|
-
import { join
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
import { join as join2 } from "node:path";
|
|
19
|
+
import { tmpdir } from "node:os";
|
|
20
|
+
import { createHash } from "node:crypto";
|
|
21
|
+
|
|
22
|
+
// ../project-root-resolver/dist/index.js
|
|
23
|
+
import { existsSync } from "node:fs";
|
|
24
|
+
import { dirname, join } from "node:path";
|
|
25
|
+
function hasProjectRootMarker(candidate, sentinel) {
|
|
26
|
+
return existsSync(join(candidate, ".git")) || existsSync(join(candidate, "package.json")) || existsSync(join(candidate, sentinel, ".root"));
|
|
27
|
+
}
|
|
28
|
+
function resolveProjectRoot(cwd, sentinel = ".orchestrator") {
|
|
29
|
+
const candidates = [];
|
|
30
|
+
let currentDir = cwd;
|
|
31
|
+
for (; ; ) {
|
|
32
|
+
if (existsSync(join(currentDir, sentinel))) {
|
|
33
|
+
candidates.push(currentDir);
|
|
34
|
+
}
|
|
35
|
+
const parentDir = dirname(currentDir);
|
|
36
|
+
if (parentDir === currentDir) {
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
currentDir = parentDir;
|
|
40
|
+
}
|
|
41
|
+
const [innermost] = candidates;
|
|
42
|
+
if (innermost === void 0) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
for (const candidate of candidates) {
|
|
46
|
+
if (hasProjectRootMarker(candidate, sentinel)) {
|
|
47
|
+
return candidate;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return innermost;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// hooks/lib/api-client.ts
|
|
54
|
+
function resolveFallbackStateDir(cwd) {
|
|
55
|
+
const cwdHash = createHash("sha256").update(cwd).digest("hex").slice(0, 16);
|
|
56
|
+
return join2(tmpdir(), "orchestrator-hook-fallback", cwdHash);
|
|
57
|
+
}
|
|
58
|
+
var PROJECT_ROOT = (() => {
|
|
59
|
+
const resolved = resolveHookProjectRoot(process.cwd());
|
|
60
|
+
if (resolved !== null && hasProjectRootMarker(resolved, ".orchestrator")) {
|
|
61
|
+
return resolved;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
})();
|
|
65
|
+
var STATE_DIR_OVERRIDE = process.env.ORCHESTRATOR_STATE_DIR;
|
|
66
|
+
var STATE_DIR = STATE_DIR_OVERRIDE !== void 0 && STATE_DIR_OVERRIDE !== "" ? STATE_DIR_OVERRIDE : PROJECT_ROOT !== null ? join2(PROJECT_ROOT, ".orchestrator", ".state") : resolveFallbackStateDir(process.cwd());
|
|
67
|
+
var JWT_CACHE = join2(STATE_DIR, "jwt-token");
|
|
68
|
+
var DEBUG_LOG = join2(STATE_DIR, "hook-debug.log");
|
|
26
69
|
mkdirSync(STATE_DIR, { recursive: true });
|
|
70
|
+
if ((STATE_DIR_OVERRIDE === void 0 || STATE_DIR_OVERRIDE === "") && PROJECT_ROOT === null) {
|
|
71
|
+
log(
|
|
72
|
+
"API-CLIENT",
|
|
73
|
+
`PROJECT_ROOT: no marker-verified ".orchestrator" project root found above ${process.cwd()} \u2014 falling back to isolated state dir ${STATE_DIR} (RF-003)`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
27
76
|
var API_URL = process.env.ORCHESTRATOR_API_URL || "http://localhost:3001";
|
|
28
77
|
var AUTH_EMAIL = process.env.ORCHESTRATOR_ADMIN_EMAIL || process.env.ORCHESTRATOR_AUTH_EMAIL || "system@orchestrator.local";
|
|
29
78
|
var AUTH_PASSWORD = process.env.ORCHESTRATOR_ADMIN_PASSWORD || process.env.ORCHESTRATOR_AUTH_PASSWORD || "system123";
|
|
30
79
|
var PROJECT_ID = process.env.ORCHESTRATOR_PROJECT_ID || "";
|
|
80
|
+
function resolveHookProjectRoot(cwd) {
|
|
81
|
+
return resolveProjectRoot(cwd);
|
|
82
|
+
}
|
|
31
83
|
function log(prefix, msg) {
|
|
32
84
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "Z");
|
|
33
85
|
try {
|
|
@@ -37,11 +89,11 @@ function log(prefix, msg) {
|
|
|
37
89
|
} catch {
|
|
38
90
|
}
|
|
39
91
|
}
|
|
40
|
-
var LAST_COMPLETED_FILE =
|
|
92
|
+
var LAST_COMPLETED_FILE = join2(STATE_DIR, "last-completed-workflow.json");
|
|
41
93
|
var LAST_COMPLETED_TTL_MS = 60 * 60 * 1e3;
|
|
42
|
-
var INVOCATIONS_DIR =
|
|
94
|
+
var INVOCATIONS_DIR = join2(STATE_DIR, "invocations");
|
|
43
95
|
var INVOCATION_STATE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
44
|
-
var DISPATCH_INTENT_DIR =
|
|
96
|
+
var DISPATCH_INTENT_DIR = join2(STATE_DIR, "dispatch-intent");
|
|
45
97
|
function outputContext(hookEvent, context) {
|
|
46
98
|
console.log(
|
|
47
99
|
JSON.stringify({
|
|
@@ -82,7 +134,7 @@ function consumeInbox(inboxPath, quarantinePath, paneRole) {
|
|
|
82
134
|
const consumed = [];
|
|
83
135
|
const errors = [];
|
|
84
136
|
for (const file of files) {
|
|
85
|
-
const filePath =
|
|
137
|
+
const filePath = join3(inboxPath, file);
|
|
86
138
|
try {
|
|
87
139
|
const raw = readFileSync2(filePath, "utf-8");
|
|
88
140
|
const parsed = JSON.parse(raw);
|
|
@@ -101,7 +153,7 @@ function consumeInbox(inboxPath, quarantinePath, paneRole) {
|
|
|
101
153
|
log(HOOK, `Quarantine: ${file} \u2014 ${errMsg}`);
|
|
102
154
|
try {
|
|
103
155
|
mkdirSync2(quarantinePath, { recursive: true });
|
|
104
|
-
renameSync(filePath,
|
|
156
|
+
renameSync(filePath, join3(quarantinePath, file));
|
|
105
157
|
} catch {
|
|
106
158
|
}
|
|
107
159
|
}
|
|
@@ -211,7 +263,7 @@ function relaySidecarMessages(consumed, sprintId, paneRole, deps = { readdirSync
|
|
|
211
263
|
};
|
|
212
264
|
const sidecarAddr = `${sidecar.role}-${String(sidecar.index)}`;
|
|
213
265
|
const targetInbox = `/tmp/${sprintId}/mailbox/${sidecarAddr}/inbox`;
|
|
214
|
-
const targetPath =
|
|
266
|
+
const targetPath = join3(targetInbox, `${envelope.id}.json`);
|
|
215
267
|
deps.writeFileSync(targetPath, JSON.stringify(envelope), "utf-8");
|
|
216
268
|
log(HOOK, `Relayed task_complete to ${sidecarAddr}: ${envelope.id}`);
|
|
217
269
|
} catch (err) {
|
|
@@ -229,7 +281,7 @@ async function main() {
|
|
|
229
281
|
}
|
|
230
282
|
const inboxPath = `/tmp/${sprintId}/mailbox/${paneRole}/inbox`;
|
|
231
283
|
const quarantinePath = `/tmp/${sprintId}/mailbox/${paneRole}/quarantine`;
|
|
232
|
-
if (!
|
|
284
|
+
if (!existsSync2(inboxPath)) {
|
|
233
285
|
return;
|
|
234
286
|
}
|
|
235
287
|
let hookEventName;
|
|
@@ -1,38 +1,68 @@
|
|
|
1
1
|
import { createRequire as __createRequireShim } from 'node:module'; const require = __createRequireShim(import.meta.url);
|
|
2
2
|
|
|
3
3
|
// hooks/playwright-output-guard.ts
|
|
4
|
-
import { dirname as
|
|
4
|
+
import { dirname as dirname2, isAbsolute, normalize, resolve } from "node:path";
|
|
5
5
|
|
|
6
6
|
// hooks/lib/api-client.ts
|
|
7
7
|
import { readFileSync, writeFileSync, mkdirSync, statSync, unlinkSync, readdirSync } from "node:fs";
|
|
8
|
-
import { join as join2
|
|
9
|
-
import {
|
|
8
|
+
import { join as join2 } from "node:path";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
10
11
|
|
|
11
12
|
// ../project-root-resolver/dist/index.js
|
|
12
13
|
import { existsSync } from "node:fs";
|
|
13
14
|
import { dirname, join } from "node:path";
|
|
15
|
+
function hasProjectRootMarker(candidate, sentinel) {
|
|
16
|
+
return existsSync(join(candidate, ".git")) || existsSync(join(candidate, "package.json")) || existsSync(join(candidate, sentinel, ".root"));
|
|
17
|
+
}
|
|
14
18
|
function resolveProjectRoot(cwd, sentinel = ".orchestrator") {
|
|
19
|
+
const candidates = [];
|
|
15
20
|
let currentDir = cwd;
|
|
16
21
|
for (; ; ) {
|
|
17
22
|
if (existsSync(join(currentDir, sentinel))) {
|
|
18
|
-
|
|
23
|
+
candidates.push(currentDir);
|
|
19
24
|
}
|
|
20
25
|
const parentDir = dirname(currentDir);
|
|
21
26
|
if (parentDir === currentDir) {
|
|
22
|
-
|
|
27
|
+
break;
|
|
23
28
|
}
|
|
24
29
|
currentDir = parentDir;
|
|
25
30
|
}
|
|
31
|
+
const [innermost] = candidates;
|
|
32
|
+
if (innermost === void 0) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
for (const candidate of candidates) {
|
|
36
|
+
if (hasProjectRootMarker(candidate, sentinel)) {
|
|
37
|
+
return candidate;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return innermost;
|
|
26
41
|
}
|
|
27
42
|
|
|
28
43
|
// hooks/lib/api-client.ts
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
44
|
+
function resolveFallbackStateDir(cwd) {
|
|
45
|
+
const cwdHash = createHash("sha256").update(cwd).digest("hex").slice(0, 16);
|
|
46
|
+
return join2(tmpdir(), "orchestrator-hook-fallback", cwdHash);
|
|
47
|
+
}
|
|
48
|
+
var PROJECT_ROOT = (() => {
|
|
49
|
+
const resolved = resolveHookProjectRoot(process.cwd());
|
|
50
|
+
if (resolved !== null && hasProjectRootMarker(resolved, ".orchestrator")) {
|
|
51
|
+
return resolved;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
})();
|
|
55
|
+
var STATE_DIR_OVERRIDE = process.env.ORCHESTRATOR_STATE_DIR;
|
|
56
|
+
var STATE_DIR = STATE_DIR_OVERRIDE !== void 0 && STATE_DIR_OVERRIDE !== "" ? STATE_DIR_OVERRIDE : PROJECT_ROOT !== null ? join2(PROJECT_ROOT, ".orchestrator", ".state") : resolveFallbackStateDir(process.cwd());
|
|
33
57
|
var JWT_CACHE = join2(STATE_DIR, "jwt-token");
|
|
34
58
|
var DEBUG_LOG = join2(STATE_DIR, "hook-debug.log");
|
|
35
59
|
mkdirSync(STATE_DIR, { recursive: true });
|
|
60
|
+
if ((STATE_DIR_OVERRIDE === void 0 || STATE_DIR_OVERRIDE === "") && PROJECT_ROOT === null) {
|
|
61
|
+
log(
|
|
62
|
+
"API-CLIENT",
|
|
63
|
+
`PROJECT_ROOT: no marker-verified ".orchestrator" project root found above ${process.cwd()} \u2014 falling back to isolated state dir ${STATE_DIR} (RF-003)`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
36
66
|
var API_URL = process.env.ORCHESTRATOR_API_URL || "http://localhost:3001";
|
|
37
67
|
var AUTH_EMAIL = process.env.ORCHESTRATOR_ADMIN_EMAIL || process.env.ORCHESTRATOR_AUTH_EMAIL || "system@orchestrator.local";
|
|
38
68
|
var AUTH_PASSWORD = process.env.ORCHESTRATOR_ADMIN_PASSWORD || process.env.ORCHESTRATOR_AUTH_PASSWORD || "system123";
|
|
@@ -95,7 +125,7 @@ var GUIDANCE = 'Playwright Output Guard: screenshots n\xE3o podem cair na raiz d
|
|
|
95
125
|
function evaluateFilename(filename) {
|
|
96
126
|
if (!filename) return { decision: "allow" };
|
|
97
127
|
const resolved = isAbsolute(filename) ? normalize(filename) : resolve(PROJECT_ROOT2, filename);
|
|
98
|
-
const dir =
|
|
128
|
+
const dir = dirname2(resolved);
|
|
99
129
|
if (dir === PROJECT_ROOT2) {
|
|
100
130
|
return { decision: "deny", reason: GUIDANCE };
|
|
101
131
|
}
|
package/hooks/post-implement.mjs
CHANGED
|
@@ -2,34 +2,64 @@ import { createRequire as __createRequireShim } from 'node:module'; const requir
|
|
|
2
2
|
|
|
3
3
|
// hooks/lib/api-client.ts
|
|
4
4
|
import { readFileSync, writeFileSync, mkdirSync, statSync, unlinkSync, readdirSync } from "node:fs";
|
|
5
|
-
import { join as join2
|
|
6
|
-
import {
|
|
5
|
+
import { join as join2 } from "node:path";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
7
8
|
|
|
8
9
|
// ../project-root-resolver/dist/index.js
|
|
9
10
|
import { existsSync } from "node:fs";
|
|
10
11
|
import { dirname, join } from "node:path";
|
|
12
|
+
function hasProjectRootMarker(candidate, sentinel) {
|
|
13
|
+
return existsSync(join(candidate, ".git")) || existsSync(join(candidate, "package.json")) || existsSync(join(candidate, sentinel, ".root"));
|
|
14
|
+
}
|
|
11
15
|
function resolveProjectRoot(cwd, sentinel = ".orchestrator") {
|
|
16
|
+
const candidates = [];
|
|
12
17
|
let currentDir = cwd;
|
|
13
18
|
for (; ; ) {
|
|
14
19
|
if (existsSync(join(currentDir, sentinel))) {
|
|
15
|
-
|
|
20
|
+
candidates.push(currentDir);
|
|
16
21
|
}
|
|
17
22
|
const parentDir = dirname(currentDir);
|
|
18
23
|
if (parentDir === currentDir) {
|
|
19
|
-
|
|
24
|
+
break;
|
|
20
25
|
}
|
|
21
26
|
currentDir = parentDir;
|
|
22
27
|
}
|
|
28
|
+
const [innermost] = candidates;
|
|
29
|
+
if (innermost === void 0) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
for (const candidate of candidates) {
|
|
33
|
+
if (hasProjectRootMarker(candidate, sentinel)) {
|
|
34
|
+
return candidate;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return innermost;
|
|
23
38
|
}
|
|
24
39
|
|
|
25
40
|
// hooks/lib/api-client.ts
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
function resolveFallbackStateDir(cwd) {
|
|
42
|
+
const cwdHash = createHash("sha256").update(cwd).digest("hex").slice(0, 16);
|
|
43
|
+
return join2(tmpdir(), "orchestrator-hook-fallback", cwdHash);
|
|
44
|
+
}
|
|
45
|
+
var PROJECT_ROOT = (() => {
|
|
46
|
+
const resolved = resolveHookProjectRoot(process.cwd());
|
|
47
|
+
if (resolved !== null && hasProjectRootMarker(resolved, ".orchestrator")) {
|
|
48
|
+
return resolved;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
})();
|
|
52
|
+
var STATE_DIR_OVERRIDE = process.env.ORCHESTRATOR_STATE_DIR;
|
|
53
|
+
var STATE_DIR = STATE_DIR_OVERRIDE !== void 0 && STATE_DIR_OVERRIDE !== "" ? STATE_DIR_OVERRIDE : PROJECT_ROOT !== null ? join2(PROJECT_ROOT, ".orchestrator", ".state") : resolveFallbackStateDir(process.cwd());
|
|
30
54
|
var JWT_CACHE = join2(STATE_DIR, "jwt-token");
|
|
31
55
|
var DEBUG_LOG = join2(STATE_DIR, "hook-debug.log");
|
|
32
56
|
mkdirSync(STATE_DIR, { recursive: true });
|
|
57
|
+
if ((STATE_DIR_OVERRIDE === void 0 || STATE_DIR_OVERRIDE === "") && PROJECT_ROOT === null) {
|
|
58
|
+
log(
|
|
59
|
+
"API-CLIENT",
|
|
60
|
+
`PROJECT_ROOT: no marker-verified ".orchestrator" project root found above ${process.cwd()} \u2014 falling back to isolated state dir ${STATE_DIR} (RF-003)`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
33
63
|
var API_URL = process.env.ORCHESTRATOR_API_URL || "http://localhost:3001";
|
|
34
64
|
var AUTH_EMAIL = process.env.ORCHESTRATOR_ADMIN_EMAIL || process.env.ORCHESTRATOR_AUTH_EMAIL || "system@orchestrator.local";
|
|
35
65
|
var AUTH_PASSWORD = process.env.ORCHESTRATOR_ADMIN_PASSWORD || process.env.ORCHESTRATOR_AUTH_PASSWORD || "system123";
|
|
@@ -1,38 +1,68 @@
|
|
|
1
1
|
import { createRequire as __createRequireShim } from 'node:module'; const require = __createRequireShim(import.meta.url);
|
|
2
2
|
|
|
3
3
|
// hooks/pre-agent-dispatch.ts
|
|
4
|
-
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
5
5
|
|
|
6
6
|
// hooks/lib/api-client.ts
|
|
7
7
|
import { readFileSync, writeFileSync, mkdirSync, statSync, unlinkSync, readdirSync } from "node:fs";
|
|
8
|
-
import { join as join2
|
|
9
|
-
import {
|
|
8
|
+
import { join as join2 } from "node:path";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
10
11
|
|
|
11
12
|
// ../project-root-resolver/dist/index.js
|
|
12
13
|
import { existsSync } from "node:fs";
|
|
13
14
|
import { dirname, join } from "node:path";
|
|
15
|
+
function hasProjectRootMarker(candidate, sentinel) {
|
|
16
|
+
return existsSync(join(candidate, ".git")) || existsSync(join(candidate, "package.json")) || existsSync(join(candidate, sentinel, ".root"));
|
|
17
|
+
}
|
|
14
18
|
function resolveProjectRoot(cwd, sentinel = ".orchestrator") {
|
|
19
|
+
const candidates = [];
|
|
15
20
|
let currentDir = cwd;
|
|
16
21
|
for (; ; ) {
|
|
17
22
|
if (existsSync(join(currentDir, sentinel))) {
|
|
18
|
-
|
|
23
|
+
candidates.push(currentDir);
|
|
19
24
|
}
|
|
20
25
|
const parentDir = dirname(currentDir);
|
|
21
26
|
if (parentDir === currentDir) {
|
|
22
|
-
|
|
27
|
+
break;
|
|
23
28
|
}
|
|
24
29
|
currentDir = parentDir;
|
|
25
30
|
}
|
|
31
|
+
const [innermost] = candidates;
|
|
32
|
+
if (innermost === void 0) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
for (const candidate of candidates) {
|
|
36
|
+
if (hasProjectRootMarker(candidate, sentinel)) {
|
|
37
|
+
return candidate;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return innermost;
|
|
26
41
|
}
|
|
27
42
|
|
|
28
43
|
// hooks/lib/api-client.ts
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
44
|
+
function resolveFallbackStateDir(cwd) {
|
|
45
|
+
const cwdHash = createHash("sha256").update(cwd).digest("hex").slice(0, 16);
|
|
46
|
+
return join2(tmpdir(), "orchestrator-hook-fallback", cwdHash);
|
|
47
|
+
}
|
|
48
|
+
var PROJECT_ROOT = (() => {
|
|
49
|
+
const resolved = resolveHookProjectRoot(process.cwd());
|
|
50
|
+
if (resolved !== null && hasProjectRootMarker(resolved, ".orchestrator")) {
|
|
51
|
+
return resolved;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
})();
|
|
55
|
+
var STATE_DIR_OVERRIDE = process.env.ORCHESTRATOR_STATE_DIR;
|
|
56
|
+
var STATE_DIR = STATE_DIR_OVERRIDE !== void 0 && STATE_DIR_OVERRIDE !== "" ? STATE_DIR_OVERRIDE : PROJECT_ROOT !== null ? join2(PROJECT_ROOT, ".orchestrator", ".state") : resolveFallbackStateDir(process.cwd());
|
|
33
57
|
var JWT_CACHE = join2(STATE_DIR, "jwt-token");
|
|
34
58
|
var DEBUG_LOG = join2(STATE_DIR, "hook-debug.log");
|
|
35
59
|
mkdirSync(STATE_DIR, { recursive: true });
|
|
60
|
+
if ((STATE_DIR_OVERRIDE === void 0 || STATE_DIR_OVERRIDE === "") && PROJECT_ROOT === null) {
|
|
61
|
+
log(
|
|
62
|
+
"API-CLIENT",
|
|
63
|
+
`PROJECT_ROOT: no marker-verified ".orchestrator" project root found above ${process.cwd()} \u2014 falling back to isolated state dir ${STATE_DIR} (RF-003)`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
36
66
|
var API_URL = process.env.ORCHESTRATOR_API_URL || "http://localhost:3001";
|
|
37
67
|
var AUTH_EMAIL = process.env.ORCHESTRATOR_ADMIN_EMAIL || process.env.ORCHESTRATOR_AUTH_EMAIL || "system@orchestrator.local";
|
|
38
68
|
var AUTH_PASSWORD = process.env.ORCHESTRATOR_ADMIN_PASSWORD || process.env.ORCHESTRATOR_AUTH_PASSWORD || "system123";
|
|
@@ -73,6 +103,10 @@ async function ensureProjectIdResolved(token) {
|
|
|
73
103
|
);
|
|
74
104
|
}
|
|
75
105
|
}
|
|
106
|
+
function normalizeAgentName(subagentType) {
|
|
107
|
+
const i = subagentType.lastIndexOf(":");
|
|
108
|
+
return i === -1 ? subagentType : subagentType.slice(i + 1);
|
|
109
|
+
}
|
|
76
110
|
function log(prefix, msg) {
|
|
77
111
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "Z");
|
|
78
112
|
try {
|
|
@@ -211,6 +245,72 @@ function outputContext(hookEvent, context) {
|
|
|
211
245
|
);
|
|
212
246
|
}
|
|
213
247
|
|
|
248
|
+
// hooks/lib/dispatch-marker.ts
|
|
249
|
+
import { execFileSync } from "node:child_process";
|
|
250
|
+
var DISPATCH_MARKER_REF_PREFIX = "refs/orchestrator/dispatch";
|
|
251
|
+
var MAX_DISPATCH_MARKERS_PER_KEY = 20;
|
|
252
|
+
var EMPTY_COMPONENT_PLACEHOLDER = "unknown";
|
|
253
|
+
function sanitizeRefComponent(raw) {
|
|
254
|
+
const cleaned = (raw || "").replace(/[^A-Za-z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
255
|
+
return cleaned || EMPTY_COMPONENT_PLACEHOLDER;
|
|
256
|
+
}
|
|
257
|
+
function formatMarkerStamp(d) {
|
|
258
|
+
return d.toISOString().replace(/[-:.]/g, "");
|
|
259
|
+
}
|
|
260
|
+
function buildMarkerRefName(branch, agent, stamp) {
|
|
261
|
+
const branchPart = sanitizeRefComponent(branch);
|
|
262
|
+
const agentPart = sanitizeRefComponent(agent);
|
|
263
|
+
const stampPart = sanitizeRefComponent(stamp);
|
|
264
|
+
return `${DISPATCH_MARKER_REF_PREFIX}/${branchPart}/${stampPart}-${agentPart}`;
|
|
265
|
+
}
|
|
266
|
+
function runGitDefault(args) {
|
|
267
|
+
return execFileSync("git", args, {
|
|
268
|
+
encoding: "utf-8",
|
|
269
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
function pruneOldestMarkers(prefix, cap, deps) {
|
|
273
|
+
const listed = deps.runGit(["for-each-ref", "--format=%(refname)", "--sort=refname", `${prefix}/`]).split("\n").map((l) => l.trim()).filter(Boolean);
|
|
274
|
+
const excess = listed.length - cap;
|
|
275
|
+
if (excess <= 0) return [];
|
|
276
|
+
const doomed = listed.slice(0, excess);
|
|
277
|
+
for (const ref of doomed) {
|
|
278
|
+
deps.runGit(["update-ref", "-d", ref]);
|
|
279
|
+
}
|
|
280
|
+
return doomed;
|
|
281
|
+
}
|
|
282
|
+
function writeDispatchBoundaryMarker(agentName, branch, deps) {
|
|
283
|
+
let ref = "";
|
|
284
|
+
try {
|
|
285
|
+
if (sanitizeRefComponent(agentName) === EMPTY_COMPONENT_PLACEHOLDER) {
|
|
286
|
+
return {
|
|
287
|
+
status: "failed",
|
|
288
|
+
reason: `refusing to write an unattributable marker (agent name "${agentName}")`
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
const sha = deps.runGit(["rev-parse", "HEAD"]).trim();
|
|
292
|
+
if (!/^[0-9a-f]{40}$/.test(sha)) {
|
|
293
|
+
return { status: "failed", reason: `HEAD did not resolve to a 40-hex sha (got "${sha}")` };
|
|
294
|
+
}
|
|
295
|
+
const stamp = formatMarkerStamp((deps.now ?? (() => /* @__PURE__ */ new Date()))());
|
|
296
|
+
ref = buildMarkerRefName(branch, agentName, stamp);
|
|
297
|
+
deps.runGit(["update-ref", ref, sha]);
|
|
298
|
+
let pruned = [];
|
|
299
|
+
try {
|
|
300
|
+
pruned = pruneOldestMarkers(
|
|
301
|
+
`${DISPATCH_MARKER_REF_PREFIX}/${sanitizeRefComponent(branch)}`,
|
|
302
|
+
MAX_DISPATCH_MARKERS_PER_KEY,
|
|
303
|
+
deps
|
|
304
|
+
);
|
|
305
|
+
} catch {
|
|
306
|
+
pruned = [];
|
|
307
|
+
}
|
|
308
|
+
return { status: "written", ref, sha, pruned };
|
|
309
|
+
} catch (err) {
|
|
310
|
+
return { status: "failed", reason: `${ref ? `ref="${ref}" ` : ""}${String(err)}` };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
214
314
|
// hooks/pre-agent-dispatch.ts
|
|
215
315
|
var HOOK = "PRE-AGENT-DISPATCH";
|
|
216
316
|
var KNOWN_AGENTS = [
|
|
@@ -232,10 +332,6 @@ var COMMITTING_AGENTS = [
|
|
|
232
332
|
"frontend-engineer"
|
|
233
333
|
];
|
|
234
334
|
var PROTECTED_BRANCHES = ["main", "master"];
|
|
235
|
-
function normalizeAgentName(subagentType) {
|
|
236
|
-
const i = subagentType.lastIndexOf(":");
|
|
237
|
-
return i === -1 ? subagentType : subagentType.slice(i + 1);
|
|
238
|
-
}
|
|
239
335
|
function isCommittingAgent(subagentType) {
|
|
240
336
|
if (!subagentType) return false;
|
|
241
337
|
const t = normalizeAgentName(subagentType).toLowerCase();
|
|
@@ -274,7 +370,7 @@ function evaluateDispatch(ctx) {
|
|
|
274
370
|
}
|
|
275
371
|
function readBranchFromGit() {
|
|
276
372
|
try {
|
|
277
|
-
return
|
|
373
|
+
return execFileSync2("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
278
374
|
encoding: "utf-8",
|
|
279
375
|
stdio: ["ignore", "pipe", "ignore"]
|
|
280
376
|
}).trim();
|
|
@@ -288,7 +384,8 @@ var defaultDeps = {
|
|
|
288
384
|
log,
|
|
289
385
|
readBranch: readBranchFromGit,
|
|
290
386
|
getActiveWorkflow,
|
|
291
|
-
writeDispatchIntent
|
|
387
|
+
writeDispatchIntent,
|
|
388
|
+
writeDispatchMarker: (agentName, branch) => writeDispatchBoundaryMarker(agentName, branch, { runGit: runGitDefault })
|
|
292
389
|
};
|
|
293
390
|
var DISPATCH_INTENT_RESOLVE_TIMEOUT_MS = 3e3;
|
|
294
391
|
async function stampDispatchIntent(deps) {
|
|
@@ -310,6 +407,29 @@ async function stampDispatchIntent(deps) {
|
|
|
310
407
|
deps.log(HOOK, `[TD-205] stampDispatchIntent failed (fail-open): ${String(err)}`);
|
|
311
408
|
}
|
|
312
409
|
}
|
|
410
|
+
function markDispatchBoundary(subagentType, branch, deps) {
|
|
411
|
+
if (!deps.writeDispatchMarker) return [];
|
|
412
|
+
if (!isCommittingAgent(subagentType)) return [];
|
|
413
|
+
try {
|
|
414
|
+
const outcome = deps.writeDispatchMarker(subagentType, branch);
|
|
415
|
+
if (outcome.status === "written") {
|
|
416
|
+
deps.log(HOOK, `[S445] boundary marker ${outcome.ref} -> ${outcome.sha}`);
|
|
417
|
+
if (outcome.pruned.length > 0) {
|
|
418
|
+
deps.log(HOOK, `[S445] pruned ${outcome.pruned.length} marker(s) over retention cap`);
|
|
419
|
+
}
|
|
420
|
+
return [];
|
|
421
|
+
}
|
|
422
|
+
deps.log(HOOK, `[S445] boundary marker FAILED: ${outcome.reason}`);
|
|
423
|
+
return [
|
|
424
|
+
`[BOUNDARY-MARKER][S445] Could not record the pre-dispatch git coordinate for '${subagentType}' on '${branch}': ${outcome.reason}. The dispatch proceeds (this never blocks), but there will be NO ref to 'git reset --hard' back to for this agent's work.`
|
|
425
|
+
];
|
|
426
|
+
} catch (err) {
|
|
427
|
+
deps.log(HOOK, `[S445] boundary marker threw (fail-open): ${String(err)}`);
|
|
428
|
+
return [
|
|
429
|
+
`[BOUNDARY-MARKER][S445] Boundary-marker step threw for '${subagentType}': ${String(err)}. Dispatch proceeds; no rollback coordinate was recorded.`
|
|
430
|
+
];
|
|
431
|
+
}
|
|
432
|
+
}
|
|
313
433
|
async function mainWithDeps(deps) {
|
|
314
434
|
const stdin = await deps.readStdin();
|
|
315
435
|
const toolInput = getField(stdin, "tool_input") || "";
|
|
@@ -317,6 +437,7 @@ async function mainWithDeps(deps) {
|
|
|
317
437
|
const prompt = getField(stdin, "tool_input.prompt") || getField(stdin, "tool_input.description") || (typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput));
|
|
318
438
|
const branch = deps.readBranch();
|
|
319
439
|
const warnings = evaluateDispatch({ subagentType, prompt, branch });
|
|
440
|
+
warnings.push(...markDispatchBoundary(subagentType, branch, deps));
|
|
320
441
|
await stampDispatchIntent(deps);
|
|
321
442
|
if (warnings.length > 0) {
|
|
322
443
|
deps.outputContext(HOOK, warnings.join("\n"));
|
|
@@ -340,7 +461,7 @@ export {
|
|
|
340
461
|
isCommittingAgent,
|
|
341
462
|
isProtectedBranch,
|
|
342
463
|
mainWithDeps,
|
|
343
|
-
|
|
464
|
+
markDispatchBoundary,
|
|
344
465
|
parseBranchFromHead,
|
|
345
466
|
stampDispatchIntent
|
|
346
467
|
};
|