oira666_pi-subagent 0.2.19 → 0.2.21
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/LICENSE +21 -21
- package/README.md +456 -456
- package/agents/code-architect.md +22 -22
- package/agents/code-reviwer.md +21 -21
- package/agents/code-writer.md +16 -16
- package/agents.ts +188 -188
- package/index.ts +360 -32
- package/package.json +74 -74
- package/render.ts +471 -463
- package/resume.ts +227 -228
- package/runner.ts +1059 -1028
- package/shared.ts +97 -97
- package/types.ts +311 -311
package/shared.ts
CHANGED
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared constants and utilities used by the subprocess runner.
|
|
3
|
-
*
|
|
4
|
-
* Single source of truth for parallel-execution defaults and helpers.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
// Parallel execution limits
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
|
|
11
|
-
export const DEFAULT_MAX_PARALLEL_TASKS = 30;
|
|
12
|
-
export const DEFAULT_MAX_CONCURRENCY = 8;
|
|
13
|
-
export const PARALLEL_HEARTBEAT_MS = 1000;
|
|
14
|
-
export const SUBAGENT_MAX_PARALLEL_TASKS_ENV = "PI_SUBAGENT_MAX_PARALLEL_TASKS";
|
|
15
|
-
export const SUBAGENT_MAX_CONCURRENCY_ENV = "PI_SUBAGENT_MAX_CONCURRENCY";
|
|
16
|
-
export const RESUME_PROVIDER = "pi-subagent-resume";
|
|
17
|
-
export const RESUME_MODEL_ID = "synthetic-tool-call";
|
|
18
|
-
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
// Shared helpers
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
|
|
23
|
-
export function parseBoolean(raw: unknown): boolean | null {
|
|
24
|
-
if (typeof raw === "boolean") return raw;
|
|
25
|
-
if (typeof raw !== "string") return null;
|
|
26
|
-
const normalized = raw.trim().toLowerCase();
|
|
27
|
-
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
28
|
-
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Parse a string into a non-negative safe integer, or null on failure. */
|
|
33
|
-
export function parseNonNegativeInt(raw: unknown): number | null {
|
|
34
|
-
if (typeof raw !== "string") return null;
|
|
35
|
-
const trimmed = raw.trim();
|
|
36
|
-
if (!/^\d+$/.test(trimmed)) return null;
|
|
37
|
-
const parsed = Number(trimmed);
|
|
38
|
-
return Number.isSafeInteger(parsed) ? parsed : null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
// Object utilities
|
|
43
|
-
// ---------------------------------------------------------------------------
|
|
44
|
-
|
|
45
|
-
/** Returns true when `value` is a plain (non-array, non-null) object. */
|
|
46
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
47
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Recursively merge `source` into a deep clone of `target`.
|
|
52
|
-
*
|
|
53
|
-
* Rules:
|
|
54
|
-
* - Plain-object values are merged recursively.
|
|
55
|
-
* - All other values (primitives, arrays, class instances) from `source`
|
|
56
|
-
* overwrite the corresponding key in `target`.
|
|
57
|
-
* - Keys present only in `target` are preserved unchanged.
|
|
58
|
-
* - The original `target` and `source` objects are never mutated.
|
|
59
|
-
*/
|
|
60
|
-
export function deepMerge(target: object, source: object): object {
|
|
61
|
-
const output: Record<string, unknown> = { ...(target as Record<string, unknown>) };
|
|
62
|
-
for (const [key, srcVal] of Object.entries(source as Record<string, unknown>)) {
|
|
63
|
-
const tgtVal = (output as Record<string, unknown>)[key];
|
|
64
|
-
if (isPlainObject(srcVal) && isPlainObject(tgtVal)) {
|
|
65
|
-
output[key] = deepMerge(tgtVal, srcVal);
|
|
66
|
-
} else {
|
|
67
|
-
output[key] = srcVal;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return output;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ---------------------------------------------------------------------------
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Map over items with a bounded number of concurrent async workers.
|
|
77
|
-
* Order of results matches order of input regardless of completion order.
|
|
78
|
-
*/
|
|
79
|
-
export async function mapConcurrent<TIn, TOut>(
|
|
80
|
-
items: TIn[],
|
|
81
|
-
concurrency: number,
|
|
82
|
-
fn: (item: TIn, index: number) => Promise<TOut>,
|
|
83
|
-
): Promise<TOut[]> {
|
|
84
|
-
if (items.length === 0) return [];
|
|
85
|
-
const limit = Math.max(1, Math.min(concurrency, items.length));
|
|
86
|
-
const results: TOut[] = new Array(items.length);
|
|
87
|
-
let nextIndex = 0;
|
|
88
|
-
const worker = async () => {
|
|
89
|
-
while (true) {
|
|
90
|
-
const i = nextIndex++;
|
|
91
|
-
if (i >= items.length) return;
|
|
92
|
-
results[i] = await fn(items[i], i);
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
await Promise.all(Array.from({ length: limit }, () => worker()));
|
|
96
|
-
return results;
|
|
97
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Shared constants and utilities used by the subprocess runner.
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for parallel-execution defaults and helpers.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Parallel execution limits
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_MAX_PARALLEL_TASKS = 30;
|
|
12
|
+
export const DEFAULT_MAX_CONCURRENCY = 8;
|
|
13
|
+
export const PARALLEL_HEARTBEAT_MS = 1000;
|
|
14
|
+
export const SUBAGENT_MAX_PARALLEL_TASKS_ENV = "PI_SUBAGENT_MAX_PARALLEL_TASKS";
|
|
15
|
+
export const SUBAGENT_MAX_CONCURRENCY_ENV = "PI_SUBAGENT_MAX_CONCURRENCY";
|
|
16
|
+
export const RESUME_PROVIDER = "pi-subagent-resume";
|
|
17
|
+
export const RESUME_MODEL_ID = "synthetic-tool-call";
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Shared helpers
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
export function parseBoolean(raw: unknown): boolean | null {
|
|
24
|
+
if (typeof raw === "boolean") return raw;
|
|
25
|
+
if (typeof raw !== "string") return null;
|
|
26
|
+
const normalized = raw.trim().toLowerCase();
|
|
27
|
+
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
28
|
+
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Parse a string into a non-negative safe integer, or null on failure. */
|
|
33
|
+
export function parseNonNegativeInt(raw: unknown): number | null {
|
|
34
|
+
if (typeof raw !== "string") return null;
|
|
35
|
+
const trimmed = raw.trim();
|
|
36
|
+
if (!/^\d+$/.test(trimmed)) return null;
|
|
37
|
+
const parsed = Number(trimmed);
|
|
38
|
+
return Number.isSafeInteger(parsed) ? parsed : null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Object utilities
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
/** Returns true when `value` is a plain (non-array, non-null) object. */
|
|
46
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
47
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Recursively merge `source` into a deep clone of `target`.
|
|
52
|
+
*
|
|
53
|
+
* Rules:
|
|
54
|
+
* - Plain-object values are merged recursively.
|
|
55
|
+
* - All other values (primitives, arrays, class instances) from `source`
|
|
56
|
+
* overwrite the corresponding key in `target`.
|
|
57
|
+
* - Keys present only in `target` are preserved unchanged.
|
|
58
|
+
* - The original `target` and `source` objects are never mutated.
|
|
59
|
+
*/
|
|
60
|
+
export function deepMerge(target: object, source: object): object {
|
|
61
|
+
const output: Record<string, unknown> = { ...(target as Record<string, unknown>) };
|
|
62
|
+
for (const [key, srcVal] of Object.entries(source as Record<string, unknown>)) {
|
|
63
|
+
const tgtVal = (output as Record<string, unknown>)[key];
|
|
64
|
+
if (isPlainObject(srcVal) && isPlainObject(tgtVal)) {
|
|
65
|
+
output[key] = deepMerge(tgtVal, srcVal);
|
|
66
|
+
} else {
|
|
67
|
+
output[key] = srcVal;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return output;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Map over items with a bounded number of concurrent async workers.
|
|
77
|
+
* Order of results matches order of input regardless of completion order.
|
|
78
|
+
*/
|
|
79
|
+
export async function mapConcurrent<TIn, TOut>(
|
|
80
|
+
items: TIn[],
|
|
81
|
+
concurrency: number,
|
|
82
|
+
fn: (item: TIn, index: number) => Promise<TOut>,
|
|
83
|
+
): Promise<TOut[]> {
|
|
84
|
+
if (items.length === 0) return [];
|
|
85
|
+
const limit = Math.max(1, Math.min(concurrency, items.length));
|
|
86
|
+
const results: TOut[] = new Array(items.length);
|
|
87
|
+
let nextIndex = 0;
|
|
88
|
+
const worker = async () => {
|
|
89
|
+
while (true) {
|
|
90
|
+
const i = nextIndex++;
|
|
91
|
+
if (i >= items.length) return;
|
|
92
|
+
results[i] = await fn(items[i], i);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
await Promise.all(Array.from({ length: limit }, () => worker()));
|
|
96
|
+
return results;
|
|
97
|
+
}
|