pi-harness-runtime 1.1.148 → 1.1.152
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 +7 -0
- package/harness/key-loader.ts +84 -0
- package/harness/langchain/agents.ts +65 -15
- package/harness/langchain/daemon.ts +58 -30
- package/harness/langchain/graph.ts +55 -7
- package/harness/langchain/jev-logger.ts +274 -0
- package/harness/langchain/run.ts +64 -161
- package/harness/langchain/surge.test.ts +42 -0
- package/harness/langchain/surge.ts +22 -1
- package/index.ts +65 -26
- package/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/dist/extension.d.ts +19 -0
- package/packages/agent-policy/dist/extension.d.ts.map +1 -0
- package/packages/agent-policy/dist/extension.js +322 -0
- package/packages/agent-policy/dist/extension.js.map +1 -0
- package/packages/agent-policy/dist/index.d.ts +25 -0
- package/packages/agent-policy/dist/index.d.ts.map +1 -0
- package/packages/agent-policy/dist/index.js +27 -0
- package/packages/agent-policy/dist/index.js.map +1 -0
- package/packages/agent-policy/dist/policy-loader.d.ts +90 -0
- package/packages/agent-policy/dist/policy-loader.d.ts.map +1 -0
- package/packages/agent-policy/dist/policy-loader.js +331 -0
- package/packages/agent-policy/dist/policy-loader.js.map +1 -0
- package/packages/agent-policy/dist/policy-renderer.d.ts +42 -0
- package/packages/agent-policy/dist/policy-renderer.d.ts.map +1 -0
- package/packages/agent-policy/dist/policy-renderer.js +295 -0
- package/packages/agent-policy/dist/policy-renderer.js.map +1 -0
- package/packages/agent-policy/dist/policy-store.d.ts +99 -0
- package/packages/agent-policy/dist/policy-store.d.ts.map +1 -0
- package/packages/agent-policy/dist/policy-store.js +244 -0
- package/packages/agent-policy/dist/policy-store.js.map +1 -0
- package/packages/agent-policy/dist/types.d.ts +237 -0
- package/packages/agent-policy/dist/types.d.ts.map +1 -0
- package/packages/agent-policy/dist/types.js +130 -0
- package/packages/agent-policy/dist/types.js.map +1 -0
- package/packages/agent-policy/package.json +45 -0
- package/packages/agent-policy/src/extension.ts +413 -0
- package/packages/agent-policy/src/index.ts +52 -0
- package/packages/agent-policy/src/policy-loader.ts +398 -0
- package/packages/agent-policy/src/policy-renderer.ts +362 -0
- package/packages/agent-policy/src/policy-store.ts +319 -0
- package/packages/agent-policy/src/types.ts +385 -0
- package/packages/agent-policy/test/policy.test.ts +383 -0
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/dist/src/parser.d.ts.map +1 -1
- package/packages/codex-watcher/dist/src/parser.js +35 -0
- package/packages/codex-watcher/dist/src/parser.js.map +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/codex-watcher/src/parser.ts +41 -0
- package/packages/config-capture/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/dist/adapters/telegram-adapter.d.ts +5 -0
- package/packages/notification/dist/adapters/telegram-adapter.d.ts.map +1 -1
- package/packages/notification/dist/adapters/telegram-adapter.js +54 -0
- package/packages/notification/dist/adapters/telegram-adapter.js.map +1 -1
- package/packages/notification/dist/notification-center.d.ts +13 -0
- package/packages/notification/dist/notification-center.d.ts.map +1 -1
- package/packages/notification/dist/notification-center.js +36 -0
- package/packages/notification/dist/notification-center.js.map +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/notification/src/adapters/telegram-adapter.ts +71 -0
- package/packages/notification/src/notification-center.ts +53 -0
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
- package/proactive-compact.ts +2 -2
- package/scripts/debug-codex-integration.ts +203 -0
- package/scripts/sync-keys-from-pi.ts +188 -0
- package/scripts/test-codex-session.ts +23 -0
- package/scripts/test-openai.ts +14 -0
- package/scripts/test-telegram.ts +34 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [1.1.139](https://github.com/ManotLuijiu/pi-harness-runtime/compare/v1.1.150...v1.1.139) (2026-10-05)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **agent-policy:** Add mandatory policy delivery and mutation gating ([19d2399](https://github.com/ManotLuijiu/pi-harness-runtime/commit/19d23997d6de8e62bf97a67daf51b5cba42c9615))
|
|
11
|
+
|
|
5
12
|
### [1.1.138](https://github.com/ManotLuijiu/pi-harness-runtime/compare/v1.1.147...v1.1.138) (2026-10-04)
|
|
6
13
|
|
|
7
14
|
### [1.1.75](https://github.com/ManotLuijiu/pi-harness-runtime/compare/v1.1.95...v1.1.75) (2026-09-29)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Key Loader — Loads API keys from ~/.pi-harness-runtime/keys/ into process.env
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import { loadKeys } from "./key-loader.js";
|
|
6
|
+
* loadKeys(); // Loads all keys into process.env
|
|
7
|
+
*
|
|
8
|
+
* Environment variables set:
|
|
9
|
+
* Telegram: TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, TELEGRAM_WEBHOOK_URL, TELEGRAM_WEBHOOK_SECRET
|
|
10
|
+
* Models: PLANNER_API_KEY, PLANNER_MODEL, PLANNER_BASE_URL
|
|
11
|
+
* GLM_API_KEY
|
|
12
|
+
* MINIMAX_API_KEY
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { homedir } from "node:os";
|
|
18
|
+
|
|
19
|
+
const KEYS_DIR = join(homedir(), ".pi-harness-runtime", "keys");
|
|
20
|
+
|
|
21
|
+
const KEY_FILE_MAP: Record<string, string> = {
|
|
22
|
+
// Telegram notification keys
|
|
23
|
+
TELEGRAM_BOT_TOKEN: "telegram-bot-token.txt",
|
|
24
|
+
TELEGRAM_CHAT_ID: "telegram-chat-id.txt",
|
|
25
|
+
TELEGRAM_WEBHOOK_URL: "telegram-webhook-url.txt",
|
|
26
|
+
TELEGRAM_WEBHOOK_SECRET: "telegram-webhook-secret.txt",
|
|
27
|
+
// Model API keys (for daemon loop)
|
|
28
|
+
PLANNER_API_KEY: "planner-api-key.txt",
|
|
29
|
+
PLANNER_MODEL: "planner-model.txt",
|
|
30
|
+
PLANNER_BASE_URL: "planner-base-url.txt",
|
|
31
|
+
GLM_API_KEY: "glm-api-key.txt",
|
|
32
|
+
GLM_MODEL: "glm-model.txt",
|
|
33
|
+
GLM_BASE_URL: "glm-base-url.txt",
|
|
34
|
+
MINIMAX_API_KEY: "minimax-api-key.txt",
|
|
35
|
+
MINIMAX_MODEL: "minimax-model.txt",
|
|
36
|
+
MINIMAX_BASE_URL: "minimax-base-url.txt",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Load all keys from ~/.pi-harness-runtime/keys/ into process.env
|
|
41
|
+
* Only sets env vars that are not already set (allows CLI args to override)
|
|
42
|
+
*/
|
|
43
|
+
export function loadKeys(): void {
|
|
44
|
+
if (!existsSync(KEYS_DIR)) {
|
|
45
|
+
console.warn(`[key-loader] Keys directory not found: ${KEYS_DIR}`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let loadedCount = 0;
|
|
50
|
+
for (const [envVar, filename] of Object.entries(KEY_FILE_MAP)) {
|
|
51
|
+
// Skip if already set via environment
|
|
52
|
+
if (process.env[envVar]) {
|
|
53
|
+
console.log(`[key-loader] ${envVar} already set via env, skipping`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const filePath = join(KEYS_DIR, filename);
|
|
58
|
+
if (existsSync(filePath)) {
|
|
59
|
+
const value = readFileSync(filePath, "utf8").trim();
|
|
60
|
+
if (value) {
|
|
61
|
+
process.env[envVar] = value;
|
|
62
|
+
loadedCount++;
|
|
63
|
+
console.log(`[key-loader] Loaded ${envVar} from ${filename}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (loadedCount > 0) {
|
|
69
|
+
console.log(`[key-loader] Loaded ${loadedCount} key(s) from ${KEYS_DIR}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get a specific key by name (without .txt extension)
|
|
75
|
+
*/
|
|
76
|
+
export function getKey(keyName: string): string | undefined {
|
|
77
|
+
const filePath = join(KEYS_DIR, `${keyName}.txt`);
|
|
78
|
+
if (existsSync(filePath)) {
|
|
79
|
+
return readFileSync(filePath, "utf8").trim();
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export default loadKeys;
|
|
@@ -37,9 +37,9 @@ export const ReviewVerdictSchema = z.object({
|
|
|
37
37
|
comments: z
|
|
38
38
|
.array(
|
|
39
39
|
z.object({
|
|
40
|
-
file: z.string().
|
|
40
|
+
file: z.string().describe("Affected file, if known"),
|
|
41
41
|
comment: z.string().describe("Actionable change request"),
|
|
42
|
-
severity: z.enum(["critical", "major", "minor"])
|
|
42
|
+
severity: z.enum(["critical", "major", "minor"]),
|
|
43
43
|
}),
|
|
44
44
|
)
|
|
45
45
|
.describe("Comments to send back to the coder (empty when approved)"),
|
|
@@ -92,16 +92,66 @@ permission to write code until finish without waiting for confirmation.
|
|
|
92
92
|
// - If stuck, fail with a clear error, do not wait silently
|
|
93
93
|
// - Output is consumed by the loop, not a human reading for guidance
|
|
94
94
|
|
|
95
|
-
const PLANNER_PROMPT = `You are the
|
|
95
|
+
const PLANNER_PROMPT = `You are Jev - the intelligent Orchestrator in an autonomous daemon loop.
|
|
96
|
+
|
|
97
|
+
## Your Role: Jev (Provider & Input Decision Engine)
|
|
98
|
+
|
|
99
|
+
You make two types of decisions:
|
|
100
|
+
|
|
101
|
+
### 1. Provider Selection (Who should handle this?)
|
|
102
|
+
|
|
103
|
+
You have two agents available:
|
|
104
|
+
- **MiniMax** (default): Fast, cheap, handles 90%+ of tasks
|
|
105
|
+
- **GPT** (escalation): Powerful, for complex reasoning only
|
|
106
|
+
|
|
107
|
+
**Use GPT only when task involves:**
|
|
108
|
+
- Complex multi-file architecture decisions
|
|
109
|
+
- Advanced concurrency/race condition debugging
|
|
110
|
+
- Security-critical code requiring deep analysis
|
|
111
|
+
- User explicitly requests: [use-gpt] or [use-gpt-only]
|
|
112
|
+
- MiniMax reviewer says: "blocked" (escalate)
|
|
113
|
+
|
|
114
|
+
**Default to MiniMax** for everything else.
|
|
115
|
+
|
|
116
|
+
### 2. User Input Decision
|
|
117
|
+
|
|
118
|
+
**Yes/No Choice** (type: "yesNo"): Binary decisions
|
|
119
|
+
- "Should I use Option A or Option B?"
|
|
120
|
+
- "Do you want me to proceed with this approach?"
|
|
121
|
+
|
|
122
|
+
**Open Question** (type: "open"): Need descriptive input
|
|
123
|
+
- "What would you like me to prioritize?"
|
|
124
|
+
- "Describe your preferred coding style"
|
|
125
|
+
|
|
126
|
+
**No User Input** (null): Proceed automatically
|
|
127
|
+
|
|
128
|
+
## Your Task
|
|
96
129
|
|
|
97
130
|
Given a feature request, produce a concise implementation plan: goals, non-goals,
|
|
98
|
-
ordered steps, files to touch, risks, and a definition-of-done
|
|
99
|
-
|
|
131
|
+
ordered steps, files to touch, risks, and a definition-of-done.
|
|
132
|
+
|
|
133
|
+
## Output Format
|
|
134
|
+
|
|
135
|
+
\`\`\`markdown
|
|
136
|
+
# Implementation Plan
|
|
137
|
+
...
|
|
138
|
+
|
|
139
|
+
[JEVO]
|
|
140
|
+
{"provider": "minimax" | "gpt", "reason": "..."}
|
|
141
|
+
|
|
142
|
+
[USER_INPUT]
|
|
143
|
+
{"type": "yesNo" | "open", "question": "...", "expectedFormat?": "..."}
|
|
144
|
+
\`\`\`
|
|
145
|
+
|
|
146
|
+
- [JEVO] is required - always specify your provider decision
|
|
147
|
+
- [USER_INPUT] is optional - only if you need user input
|
|
148
|
+
|
|
149
|
+
The loop will route based on your decisions.`;
|
|
100
150
|
|
|
101
|
-
|
|
102
|
-
The loop will route automatically.`;
|
|
151
|
+
const CODER_PROMPT = `You are the Coding Agent in an autonomous daemon loop.
|
|
103
152
|
|
|
104
|
-
|
|
153
|
+
Jev has selected the provider for this task based on task complexity.
|
|
154
|
+
Write quality code regardless of which model is running.
|
|
105
155
|
|
|
106
156
|
You receive a plan (and possibly review comments from a previous iteration) and
|
|
107
157
|
edit the actual repository files using the available tools. Address every review
|
|
@@ -124,11 +174,11 @@ IMPORTANT:
|
|
|
124
174
|
- If you cannot proceed, fail with a clear error message
|
|
125
175
|
- Your output is consumed by the loop, not read by a human for guidance`;
|
|
126
176
|
|
|
127
|
-
const REVIEWER_PROMPT = `You are the Master Code Review Agent
|
|
177
|
+
const REVIEWER_PROMPT = `You are the Master Code Review Agent in an autonomous daemon loop.
|
|
128
178
|
|
|
129
|
-
You
|
|
130
|
-
|
|
131
|
-
the
|
|
179
|
+
You review the coder's output against the plan. Be strict: request changes only for
|
|
180
|
+
real problems. Output a structured verdict — the loop reads it automatically and
|
|
181
|
+
routes the coder back to fix or approves.
|
|
132
182
|
|
|
133
183
|
IMPORTANT:
|
|
134
184
|
- Do NOT output "Next Steps" or "Looks good, waiting for approval"
|
|
@@ -136,7 +186,7 @@ IMPORTANT:
|
|
|
136
186
|
- If you need clarification, fail with a clear error, do not wait silently
|
|
137
187
|
- Respond via the structured schema`;
|
|
138
188
|
|
|
139
|
-
const SUPERVISOR_PROMPT = `You are the Supervisor
|
|
189
|
+
const SUPERVISOR_PROMPT = `You are the Supervisor in an autonomous daemon loop.
|
|
140
190
|
Coordinate the workflow automatically. The loop routes to the reviewer and back
|
|
141
191
|
without human input.
|
|
142
192
|
|
|
@@ -283,7 +333,7 @@ export async function parallelReview(
|
|
|
283
333
|
comments: unknown[],
|
|
284
334
|
type: SpecialistType,
|
|
285
335
|
): {
|
|
286
|
-
file
|
|
336
|
+
file: string;
|
|
287
337
|
comment: string;
|
|
288
338
|
severity: "critical" | "major" | "minor";
|
|
289
339
|
}[] =>
|
|
@@ -294,7 +344,7 @@ export async function parallelReview(
|
|
|
294
344
|
severity?: "critical" | "major" | "minor";
|
|
295
345
|
};
|
|
296
346
|
return {
|
|
297
|
-
file: comment.file,
|
|
347
|
+
file: comment.file ?? "",
|
|
298
348
|
comment: `[${type}] ${comment.comment}`,
|
|
299
349
|
severity: comment.severity ?? "minor",
|
|
300
350
|
};
|
|
@@ -320,18 +320,18 @@ class InboxWatcher {
|
|
|
320
320
|
}
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
323
|
+
private _seedSeen(): void {
|
|
324
|
+
if (!existsSync(this.inboxDir)) return;
|
|
325
|
+
for (const file of readdirSync(this.inboxDir)) {
|
|
326
|
+
if (!file.endsWith(".md")) continue;
|
|
327
|
+
if (file.startsWith("ack-")) continue;
|
|
328
|
+
this.seen.set(file, {
|
|
329
|
+
taskId: file,
|
|
330
|
+
source: "inbox",
|
|
331
|
+
seenAt: new Date().toISOString(),
|
|
332
|
+
});
|
|
333
|
+
}
|
|
333
334
|
}
|
|
334
|
-
}
|
|
335
335
|
|
|
336
336
|
private _poll(onTask: (task: TriggeredTask) => void): void {
|
|
337
337
|
if (!this.running) return;
|
|
@@ -1123,7 +1123,7 @@ export class LoopDaemon {
|
|
|
1123
1123
|
);
|
|
1124
1124
|
if (!approved) {
|
|
1125
1125
|
log(`Human denied approval — parking task`);
|
|
1126
|
-
this._notifyHumanReviewNeeded(
|
|
1126
|
+
await this._notifyHumanReviewNeeded(
|
|
1127
1127
|
task,
|
|
1128
1128
|
"Human denied approval for task start",
|
|
1129
1129
|
);
|
|
@@ -1179,7 +1179,8 @@ export class LoopDaemon {
|
|
|
1179
1179
|
}
|
|
1180
1180
|
return undefined; // default MemorySaver
|
|
1181
1181
|
})();
|
|
1182
|
-
|
|
1182
|
+
console.log(`[${loopId}] Using models - PLANNER=${process.env.PLANNER_MODEL} (${process.env.PLANNER_BASE_URL}), GLM=${process.env.GLM_MODEL} (${process.env.GLM_BASE_URL}), MINIMAX=${process.env.MINIMAX_MODEL} (${process.env.MINIMAX_BASE_URL})`);
|
|
1183
|
+
const loop: WriteReviewLoop = buildWriteReviewLoop(deps, {
|
|
1183
1184
|
checkpointer,
|
|
1184
1185
|
});
|
|
1185
1186
|
|
|
@@ -1195,7 +1196,7 @@ export class LoopDaemon {
|
|
|
1195
1196
|
() =>
|
|
1196
1197
|
loop.invoke(
|
|
1197
1198
|
{ request: task.request },
|
|
1198
|
-
{ configurable: { thread_id: loopId }, signal },
|
|
1199
|
+
{ configurable: { thread_id: loopId }, signal, recursionLimit: 50 },
|
|
1199
1200
|
),
|
|
1200
1201
|
{
|
|
1201
1202
|
tickMs: 30_000,
|
|
@@ -1281,7 +1282,7 @@ export class LoopDaemon {
|
|
|
1281
1282
|
log("Task timed out — releasing lease and cleaning up");
|
|
1282
1283
|
this._releaseLease(task.taskId);
|
|
1283
1284
|
this.running.delete(task.taskId);
|
|
1284
|
-
this._notifyHumanReviewNeeded(
|
|
1285
|
+
await this._notifyHumanReviewNeeded(
|
|
1285
1286
|
task,
|
|
1286
1287
|
`Task timed out after ${this.config.taskTimeoutMs}ms — manual review needed`,
|
|
1287
1288
|
);
|
|
@@ -1398,18 +1399,18 @@ export class LoopDaemon {
|
|
|
1398
1399
|
|
|
1399
1400
|
// ── Notify on blocked / max-iterations ─────────────────────────────
|
|
1400
1401
|
if (verdict === "blocked") {
|
|
1401
|
-
this._notifyHumanReviewNeeded(
|
|
1402
|
+
await this._notifyHumanReviewNeeded(
|
|
1402
1403
|
task,
|
|
1403
1404
|
`Loop blocked: ${finalState.review?.summary ?? "no details"}`,
|
|
1404
1405
|
);
|
|
1405
1406
|
} else if (verdict === "changes_requested") {
|
|
1406
|
-
this._notifyHumanReviewNeeded(
|
|
1407
|
+
await this._notifyHumanReviewNeeded(
|
|
1407
1408
|
task,
|
|
1408
1409
|
`${iterations} iteration(s) exhausted with changes still requested: ${finalState.review?.comments.map((c) => c.comment).join("; ") ?? "no details"}`,
|
|
1409
1410
|
);
|
|
1410
1411
|
} else {
|
|
1411
1412
|
// approved
|
|
1412
|
-
this._notifyReadyForClient(task);
|
|
1413
|
+
await this._notifyReadyForClient(task);
|
|
1413
1414
|
}
|
|
1414
1415
|
|
|
1415
1416
|
// ── Notify agent of loop completion (TUI todo count) ───────────────
|
|
@@ -1535,19 +1536,33 @@ export class LoopDaemon {
|
|
|
1535
1536
|
|
|
1536
1537
|
// ─── Notifications ────────────────────────────────────────────────────
|
|
1537
1538
|
|
|
1538
|
-
private _notifyHumanReviewNeeded(task: TriggeredTask, reason: string): void {
|
|
1539
|
+
private async _notifyHumanReviewNeeded(task: TriggeredTask, reason: string): Promise<void> {
|
|
1539
1540
|
if (!this.notificationCenter) return;
|
|
1540
1541
|
console.log(`[daemon] Notifying HumanReviewNeeded: ${reason}`);
|
|
1541
|
-
|
|
1542
|
-
.notify("HumanReviewNeeded", {
|
|
1542
|
+
try {
|
|
1543
|
+
const results = await this.notificationCenter.notify("HumanReviewNeeded", {
|
|
1543
1544
|
jobId: task.taskId,
|
|
1544
1545
|
requirement: task.request,
|
|
1545
1546
|
taskId: task.taskId,
|
|
1546
1547
|
error: reason,
|
|
1547
|
-
})
|
|
1548
|
-
.catch((err) => {
|
|
1549
|
-
console.warn(`[daemon] Notification failed: ${err}`);
|
|
1550
1548
|
});
|
|
1549
|
+
for (const result of results) {
|
|
1550
|
+
if (!result.success) {
|
|
1551
|
+
// Redact sensitive data — do not log token, chat ID, or full payload
|
|
1552
|
+
const category = result.error?.includes("401") || result.error?.includes("403")
|
|
1553
|
+
? "auth"
|
|
1554
|
+
: result.error?.includes("400")
|
|
1555
|
+
? "bad_request"
|
|
1556
|
+
: result.error?.includes("429")
|
|
1557
|
+
? "rate_limit"
|
|
1558
|
+
: "unknown";
|
|
1559
|
+
console.warn(`[daemon] Notification failed to ${result.channel} (${category}): ${result.error}`);
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
catch (err) {
|
|
1564
|
+
console.warn(`[daemon] Notification threw: ${err}`);
|
|
1565
|
+
}
|
|
1551
1566
|
}
|
|
1552
1567
|
|
|
1553
1568
|
private _closeBdIssue(taskId: string, verdict: string): void {
|
|
@@ -1571,18 +1586,31 @@ export class LoopDaemon {
|
|
|
1571
1586
|
}
|
|
1572
1587
|
}
|
|
1573
1588
|
|
|
1574
|
-
private _notifyReadyForClient(task: TriggeredTask): void {
|
|
1589
|
+
private async _notifyReadyForClient(task: TriggeredTask): Promise<void> {
|
|
1575
1590
|
if (!this.notificationCenter) return;
|
|
1576
1591
|
console.log(`[daemon] Notifying ReadyForClient`);
|
|
1577
|
-
|
|
1578
|
-
.notify("ReadyForClient", {
|
|
1592
|
+
try {
|
|
1593
|
+
const results = await this.notificationCenter.notify("ReadyForClient", {
|
|
1579
1594
|
jobId: task.taskId,
|
|
1580
1595
|
requirement: task.request,
|
|
1581
1596
|
taskId: task.taskId,
|
|
1582
|
-
})
|
|
1583
|
-
.catch((err) => {
|
|
1584
|
-
console.warn(`[daemon] Notification failed: ${err}`);
|
|
1585
1597
|
});
|
|
1598
|
+
for (const result of results) {
|
|
1599
|
+
if (!result.success) {
|
|
1600
|
+
const category = result.error?.includes("401") || result.error?.includes("403")
|
|
1601
|
+
? "auth"
|
|
1602
|
+
: result.error?.includes("400")
|
|
1603
|
+
? "bad_request"
|
|
1604
|
+
: result.error?.includes("429")
|
|
1605
|
+
? "rate_limit"
|
|
1606
|
+
: "unknown";
|
|
1607
|
+
console.warn(`[daemon] Notification failed to ${result.channel} (${category}): ${result.error}`);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
catch (err) {
|
|
1612
|
+
console.warn(`[daemon] Notification threw: ${err}`);
|
|
1613
|
+
}
|
|
1586
1614
|
}
|
|
1587
1615
|
|
|
1588
1616
|
/**
|
|
@@ -26,6 +26,17 @@ import {
|
|
|
26
26
|
type ReviewVerdict,
|
|
27
27
|
ReviewVerdictSchema,
|
|
28
28
|
} from "./agents.js";
|
|
29
|
+
|
|
30
|
+
// ─── Module-level state for Jev provider selection ──────────────────────────
|
|
31
|
+
let _currentProvider: "minimax" | "gpt" = "minimax";
|
|
32
|
+
|
|
33
|
+
export function setJevProvider(provider: "minimax" | "gpt"): void {
|
|
34
|
+
_currentProvider = provider;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getJevProvider(): "minimax" | "gpt" {
|
|
38
|
+
return _currentProvider;
|
|
39
|
+
}
|
|
29
40
|
import { readFileSync } from "node:fs";
|
|
30
41
|
import { WriteReviewBlackboard } from "../../packages/write-review/src/blackboard.js";
|
|
31
42
|
import { getApprovedPatternStore } from "../../packages/trajectory/src/index.js";
|
|
@@ -64,8 +75,26 @@ const LoopState = Annotation.Root({
|
|
|
64
75
|
reducer: (_prev, next) => next,
|
|
65
76
|
default: () => ({}),
|
|
66
77
|
}),
|
|
78
|
+
/** User input request - when Jev decides to ask the user.
|
|
79
|
+
* null = no user input needed (continue automatically)
|
|
80
|
+
* { type: "yesNo", question, choices? } = binary choice
|
|
81
|
+
* { type: "open", question, expectedFormat? } = free text response */
|
|
82
|
+
userInput: Annotation<UserInput | null>({
|
|
83
|
+
reducer: (_prev, next) => next,
|
|
84
|
+
default: () => null,
|
|
85
|
+
}),
|
|
86
|
+
/** User's response to the question */
|
|
87
|
+
userResponse: Annotation<string | null>({
|
|
88
|
+
reducer: (_prev, next) => next,
|
|
89
|
+
default: () => null,
|
|
90
|
+
}),
|
|
67
91
|
});
|
|
68
92
|
|
|
93
|
+
/** User input types that Jev can request */
|
|
94
|
+
export type UserInput =
|
|
95
|
+
| { type: "yesNo"; question: string; yesLabel?: string; noLabel?: string }
|
|
96
|
+
| { type: "open"; question: string; expectedFormat?: string };
|
|
97
|
+
|
|
69
98
|
export type LoopState = typeof LoopState.State;
|
|
70
99
|
|
|
71
100
|
// ─── Dependencies (inject real agents or dry-run stubs) ─────────────────────
|
|
@@ -419,16 +448,35 @@ export async function buildRealLoopDeps(
|
|
|
419
448
|
maxIterations: options.maxIterations ?? 3,
|
|
420
449
|
onStep,
|
|
421
450
|
plan: async (request) => {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
await planner.invoke({
|
|
451
|
+
// P1-2: detect autonomy signal in the request and unlock full autonomous mode
|
|
452
|
+
_directive = isAutonomyRequest(request) ? autonomyDirective() : "";
|
|
453
|
+
const plannerResponse = await planner.invoke({
|
|
426
454
|
messages: [
|
|
427
455
|
{ role: "user", content: _directive + withScoreboard(request) },
|
|
428
456
|
],
|
|
429
|
-
})
|
|
430
|
-
|
|
431
|
-
|
|
457
|
+
});
|
|
458
|
+
const planOutput = lastMessage(plannerResponse) ?? "";
|
|
459
|
+
|
|
460
|
+
// Import and use Jev logger (async - logs to file + Telegram)
|
|
461
|
+
try {
|
|
462
|
+
const { logJevDecision, parseJevDecision } = await import("./jev-logger.js");
|
|
463
|
+
const jevDecision = parseJevDecision(planOutput);
|
|
464
|
+
setJevProvider(jevDecision.provider);
|
|
465
|
+
await logJevDecision({
|
|
466
|
+
timestamp: new Date().toISOString(),
|
|
467
|
+
request,
|
|
468
|
+
provider: jevDecision.provider,
|
|
469
|
+
reason: jevDecision.reason,
|
|
470
|
+
userInput: jevDecision.userInput,
|
|
471
|
+
escalate: false,
|
|
472
|
+
raw: planOutput,
|
|
473
|
+
});
|
|
474
|
+
} catch (logErr) {
|
|
475
|
+
console.error("[JEV] Logger error:", (logErr as Error).message);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return planOutput;
|
|
479
|
+
},
|
|
432
480
|
write: async (plan, review) => {
|
|
433
481
|
const userMsg = review
|
|
434
482
|
? `## Plan\n${plan}\n\n## Review comments to address\n${review.comments
|