@aion0/forge 0.10.30 → 0.10.31
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/RELEASE_NOTES.md
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
# Forge v0.10.
|
|
1
|
+
# Forge v0.10.31
|
|
2
2
|
|
|
3
3
|
Released: 2026-06-02
|
|
4
4
|
|
|
5
|
-
## Changes since v0.10.
|
|
5
|
+
## Changes since v0.10.30
|
|
6
6
|
|
|
7
7
|
### Other
|
|
8
|
-
- fix(
|
|
9
|
-
- fix(chat): raise MAX_ITERATIONS 12→50 for complex multi-step tasks
|
|
10
|
-
- fix(chat): raise MAX_ITERATIONS 6→12 + emit sentinel when cap hit
|
|
8
|
+
- fix(watch): allow builtin tool names (no connector prefix) in start_watch
|
|
11
9
|
|
|
12
10
|
|
|
13
|
-
**Full Changelog**: https://github.com/aiwatching/forge/compare/v0.10.
|
|
11
|
+
**Full Changelog**: https://github.com/aiwatching/forge/compare/v0.10.30...v0.10.31
|
|
@@ -35,12 +35,12 @@ export function buildStartWatchTool(sessionId: string | null): StartWatchTool {
|
|
|
35
35
|
const def: BuiltinToolDef = {
|
|
36
36
|
name: 'start_watch',
|
|
37
37
|
description:
|
|
38
|
-
'Register a BACKGROUND WATCH that polls a tool until done, then posts the result back here — for long-running jobs you just kicked off (a Jenkins build, a test run, a device upgrade). Use this INSTEAD of polling in conversation: call the trigger tool, then call start_watch and STOP — Forge polls in the background and a completion message arrives in this chat. ' +
|
|
39
|
-
'Pick `poll` = the read tool that reports status (e.g. "jenkins.get_build") and `poll_args` to call it with (e.g. the build number you predicted via get_next_build_number). Give a done condition: `done_match` {path, equals} on the poll result (e.g. path "result" equals "SUCCESS"), or `done_path` (a result path that becomes truthy). You usually already saw the poll tool\'s output once, so you know the right field. Optional `fail_path` (truthy = failed). Tune `interval_sec`/`timeout_sec` to the job (build ≈ 60s / 1800s).',
|
|
38
|
+
'Register a BACKGROUND WATCH that polls a tool until done, then posts the result back here — for long-running jobs you just kicked off (a Forge pipeline, a Jenkins build, a test run, a device upgrade). Use this INSTEAD of polling in conversation: call the trigger tool, then call start_watch and STOP — Forge polls in the background and a completion message arrives in this chat. ' +
|
|
39
|
+
'Pick `poll` = the read tool that reports status. Two forms accepted: (a) connector tool "<connector>.<tool>" e.g. "jenkins.get_build", "gitlab.get_pipeline"; (b) BARE builtin name e.g. "get_pipeline_status" (Forge pipelines — pair with poll_args={pipeline_id} and done_match={path:"status",equals:"done"}). Set `poll_args` to call it with (e.g. the build number you predicted via get_next_build_number). Give a done condition: `done_match` {path, equals} on the poll result (e.g. path "result" equals "SUCCESS"), or `done_path` (a result path that becomes truthy). You usually already saw the poll tool\'s output once, so you know the right field. Optional `fail_path` (truthy = failed). Tune `interval_sec`/`timeout_sec` to the job (pipeline ≈ 30s / 1800s, build ≈ 60s / 1800s).',
|
|
40
40
|
input_schema: {
|
|
41
41
|
type: 'object',
|
|
42
42
|
properties: {
|
|
43
|
-
poll: { type: 'string', description: 'Tool to poll
|
|
43
|
+
poll: { type: 'string', description: 'Tool to poll. Either "<connector>.<tool>" e.g. "jenkins.get_build", OR a bare builtin name e.g. "get_pipeline_status" (for Forge pipelines). Must be a read/status tool.' },
|
|
44
44
|
poll_args: { type: 'object', description: 'Args to call the poll tool with each tick, e.g. {"job_path":"job/foo","build_number":18}. Concrete values, not templates.' },
|
|
45
45
|
done_match: {
|
|
46
46
|
type: 'object',
|
|
@@ -68,9 +68,16 @@ export function buildStartWatchTool(sessionId: string | null): StartWatchTool {
|
|
|
68
68
|
const a = (input ?? {}) as Record<string, any>;
|
|
69
69
|
const poll = String(a.poll || '').trim();
|
|
70
70
|
const dot = poll.indexOf('.');
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
// Two forms accepted:
|
|
72
|
+
// "connector.tool" → connector_id="connector", poll_tool="tool"
|
|
73
|
+
// "tool" → builtin (e.g. get_pipeline_status), connector_id=""
|
|
74
|
+
// The watch-runner detects empty connector_id and dispatches as a
|
|
75
|
+
// bare builtin name instead of "connector.tool".
|
|
76
|
+
const connectorId = dot >= 1 ? poll.slice(0, dot) : '';
|
|
77
|
+
const pollTool = dot >= 1 ? poll.slice(dot + 1) : poll;
|
|
78
|
+
if (!pollTool) {
|
|
79
|
+
return JSON.stringify({ ok: false, error: 'poll is required, e.g. "jenkins.get_build" or builtin "get_pipeline_status"' });
|
|
80
|
+
}
|
|
74
81
|
|
|
75
82
|
const doneMatch = a.done_match && typeof a.done_match === 'object' && a.done_match.path
|
|
76
83
|
? { path: String(a.done_match.path), ...(a.done_match.equals != null ? { equals: String(a.done_match.equals) } : {}), ...(a.done_match.contains != null ? { contains: String(a.done_match.contains) } : {}) }
|
|
@@ -83,8 +90,8 @@ export function buildStartWatchTool(sessionId: string | null): StartWatchTool {
|
|
|
83
90
|
return JSON.stringify({ ok: false, error: `active watch limit reached (${MAX_ACTIVE_WATCHES})` });
|
|
84
91
|
}
|
|
85
92
|
|
|
86
|
-
const hint = ['build_number', 'host', 'ip', 'lab', 'id', 'name'].map((k) => a.poll_args?.[k]).find((v) => v != null && v !== '');
|
|
87
|
-
const label = `${connectorId}.${pollTool}${hint != null ? ` ${hint}` : ''}`;
|
|
93
|
+
const hint = ['build_number', 'host', 'ip', 'lab', 'id', 'name', 'pipeline_id'].map((k) => a.poll_args?.[k]).find((v) => v != null && v !== '');
|
|
94
|
+
const label = `${connectorId ? `${connectorId}.${pollTool}` : pollTool}${hint != null ? ` ${hint}` : ''}`;
|
|
88
95
|
|
|
89
96
|
const w = createWatch({
|
|
90
97
|
session_id: sessionId,
|
|
@@ -93,7 +93,10 @@ export function startWatchRunner(hooks: WatchRunnerHooks = {}): void {
|
|
|
93
93
|
// preamble — the body comes back as raw JSON so done_path/done_match can
|
|
94
94
|
// see it. (Without this, every http-protocol watch would silently never
|
|
95
95
|
// hit its done condition, e.g. jenkins.get_build never resolving.)
|
|
96
|
-
|
|
96
|
+
// Empty connector_id → builtin (e.g. get_pipeline_status); dispatch
|
|
97
|
+
// bare name so dispatchTool routes to the global BUILTINS table.
|
|
98
|
+
const dispatchName = w.connector_id ? `${w.connector_id}.${w.poll_tool}` : w.poll_tool;
|
|
99
|
+
res = await dispatchTool({ id: `watch-${w.id}-${w.polls}`, name: dispatchName, input: w.poll_args }, { noTruncation: true } as any);
|
|
97
100
|
} catch (e) {
|
|
98
101
|
res = { content: String(e), is_error: true };
|
|
99
102
|
}
|
package/package.json
CHANGED