@loops-adk/core 0.2.0 → 0.3.0
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/README.md +27 -2
- package/dist/api.d.ts +14 -3
- package/dist/api.js +2 -2
- package/dist/api.js.map +1 -1
- package/dist/{chunk-WM5QVHM2.js → chunk-3PMVII43.js} +195 -5
- package/dist/chunk-3PMVII43.js.map +1 -0
- package/dist/env/command.d.ts +1 -1
- package/dist/env/docker.d.ts +1 -1
- package/dist/env/sst.d.ts +1 -1
- package/dist/index.js +16 -4
- package/dist/index.js.map +1 -1
- package/dist/{types-Cv_3ymr9.d.ts → types-CpB03Jj4.d.ts} +137 -1
- package/package.json +2 -1
- package/dist/chunk-WM5QVHM2.js.map +0 -1
|
@@ -395,6 +395,118 @@ declare function commitJob(config: CommitJobConfig): Job;
|
|
|
395
395
|
/** A deterministic step from a plain function — for glue, checks, side effects. */
|
|
396
396
|
declare function fnJob(label: string, fn: (ctx: JobContext) => Outcome | Promise<Outcome>): Job;
|
|
397
397
|
|
|
398
|
+
/**
|
|
399
|
+
* No-progress (stall) detection — the third hard stop, alongside `max` and
|
|
400
|
+
* `budget`. `max` bounds how many attempts a loop gets and `budget` bounds what
|
|
401
|
+
* they cost; neither can tell "slow but real convergence" from "the same failure
|
|
402
|
+
* five turns running". This module supplies that sensor, so a doomed loop exits
|
|
403
|
+
* at iteration N+window instead of burning everything it was given.
|
|
404
|
+
*
|
|
405
|
+
* The decision rule is NOVELTY, not change. An iteration makes progress when it
|
|
406
|
+
* reaches a state this run has never seen:
|
|
407
|
+
*
|
|
408
|
+
* - the workspace fingerprint (HEAD + pending diff + untracked content) is new
|
|
409
|
+
* — so an agent oscillating A→B→A gets no credit for the return trip;
|
|
410
|
+
* - a caller-supplied `signal` value is new — the escape hatch for loops whose
|
|
411
|
+
* progress lives outside the worktree (a queue length, a passing-test count);
|
|
412
|
+
* - the gate confidence beats its previous best by `minConfidenceDelta` — a
|
|
413
|
+
* high-water mark, so judge jitter around a flat score is not progress but
|
|
414
|
+
* slow, steady improvement accumulates until it clears the bar.
|
|
415
|
+
*
|
|
416
|
+
* `window` consecutive iterations with evidence and no novelty = stalled. The
|
|
417
|
+
* default is deliberately conservative (any channel's novelty counts): a false
|
|
418
|
+
* "stalled" on work that was actually converging is worse than one more
|
|
419
|
+
* iteration. An iteration with NO evidence channel at all (no git workspace, no
|
|
420
|
+
* confidence, no signal) is indeterminate — it neither extends nor resets the
|
|
421
|
+
* stall run, and the detector reports itself inert so the loop can warn once.
|
|
422
|
+
* Gate/review reasons are deliberately NOT compared: judge prose varies between
|
|
423
|
+
* identical verdicts, so it is quoted in the report but never used as evidence.
|
|
424
|
+
*/
|
|
425
|
+
|
|
426
|
+
interface NoProgressConfig {
|
|
427
|
+
/** Consecutive no-progress iterations before the loop stalls out. Default 3. */
|
|
428
|
+
window?: number;
|
|
429
|
+
/**
|
|
430
|
+
* How far the gate confidence must beat its previous best to count as
|
|
431
|
+
* progress (the high-water mark). Default 0.02.
|
|
432
|
+
*/
|
|
433
|
+
minConfidenceDelta?: number;
|
|
434
|
+
/**
|
|
435
|
+
* A caller-supplied progress fingerprint for state the workspace cannot see
|
|
436
|
+
* (a queue length, a passing-test count, an external resource). Returning a
|
|
437
|
+
* value this run has already produced counts as no progress; `undefined`
|
|
438
|
+
* leaves the channel out of this iteration's evidence. A throw is a bug in
|
|
439
|
+
* the definition and fails the loop, like any other guarded user code.
|
|
440
|
+
*/
|
|
441
|
+
signal?: (ctx: JobContext, last: Outcome | undefined) => string | number | undefined | Promise<string | number | undefined>;
|
|
442
|
+
/**
|
|
443
|
+
* Read the workspace fingerprint each iteration (a few git subprocesses).
|
|
444
|
+
* Default true; set false when a custom `signal` is the only honest channel.
|
|
445
|
+
*/
|
|
446
|
+
workspace?: boolean;
|
|
447
|
+
}
|
|
448
|
+
/** What `LoopConfig.noProgress` accepts: a bare window, or the full config. */
|
|
449
|
+
type NoProgressInput = number | NoProgressConfig;
|
|
450
|
+
/** The evidence a stalled loop carries out — on the outcome and the event. */
|
|
451
|
+
interface StallReport {
|
|
452
|
+
/** The configured window that was filled. */
|
|
453
|
+
window: number;
|
|
454
|
+
/** The consecutive no-progress iterations, in order. */
|
|
455
|
+
iterations: number[];
|
|
456
|
+
/** The last gate/review reason observed — what kept failing. */
|
|
457
|
+
reason: string;
|
|
458
|
+
/** Per-channel assessment of the tripping iteration. */
|
|
459
|
+
evidence: string[];
|
|
460
|
+
}
|
|
461
|
+
/** One completed, non-converged iteration as the tracker sees it. */
|
|
462
|
+
interface ProgressSample {
|
|
463
|
+
iteration: number;
|
|
464
|
+
/** Workspace fingerprint, when the workspace is a git repo. */
|
|
465
|
+
fingerprint?: string;
|
|
466
|
+
/** The confidence that gated this turn (review ?? until ?? body). */
|
|
467
|
+
confidence?: number;
|
|
468
|
+
/** The custom signal value, when a `signal` fn is configured. */
|
|
469
|
+
signal?: string;
|
|
470
|
+
/** The gate/review reason — reporting only, never evidence. */
|
|
471
|
+
reason?: string;
|
|
472
|
+
}
|
|
473
|
+
/** Resolve the `noProgress` sugar (`3` ⇒ `{ window: 3 }`) with defaults applied. */
|
|
474
|
+
declare function resolveNoProgress(input: NoProgressInput | undefined): Required<Pick<NoProgressConfig, 'window' | 'minConfidenceDelta'>> & NoProgressConfig | undefined;
|
|
475
|
+
/**
|
|
476
|
+
* The novelty tracker behind `LoopConfig.noProgress`. Feed it one sample per
|
|
477
|
+
* non-converged iteration; it returns a `StallReport` the moment `window`
|
|
478
|
+
* consecutive samples show evidence and no novelty.
|
|
479
|
+
*/
|
|
480
|
+
declare class ProgressTracker {
|
|
481
|
+
readonly window: number;
|
|
482
|
+
readonly minConfidenceDelta: number;
|
|
483
|
+
/** Every state this run has reached, namespaced by channel. */
|
|
484
|
+
private readonly seen;
|
|
485
|
+
/** Confidence high-water mark — the best score at the last progress point. */
|
|
486
|
+
private best;
|
|
487
|
+
/** The current run of consecutive no-progress iterations. */
|
|
488
|
+
private stalledRun;
|
|
489
|
+
private lastEvidence;
|
|
490
|
+
private lastReason;
|
|
491
|
+
private indeterminate;
|
|
492
|
+
private sampled;
|
|
493
|
+
constructor(cfg: {
|
|
494
|
+
window: number;
|
|
495
|
+
minConfidenceDelta: number;
|
|
496
|
+
});
|
|
497
|
+
/**
|
|
498
|
+
* Record one iteration. Returns a `StallReport` when this sample fills the
|
|
499
|
+
* window, else undefined.
|
|
500
|
+
*/
|
|
501
|
+
record(sample: ProgressSample): StallReport | undefined;
|
|
502
|
+
/**
|
|
503
|
+
* True when the detector has seen a full window of samples and none carried
|
|
504
|
+
* any evidence channel — detection is configured but cannot fire. The loop
|
|
505
|
+
* uses this to warn once instead of failing silently-inert.
|
|
506
|
+
*/
|
|
507
|
+
isInert(): boolean;
|
|
508
|
+
}
|
|
509
|
+
|
|
398
510
|
/**
|
|
399
511
|
* The Environment provider — the third axis, after Engine (where the agent
|
|
400
512
|
* thinks) and Workspace (where the code lives). Environment is where the code
|
|
@@ -595,6 +707,13 @@ interface Outcome {
|
|
|
595
707
|
data?: unknown;
|
|
596
708
|
/** Present when `status` is driven by a failure. */
|
|
597
709
|
error?: LoopError;
|
|
710
|
+
/**
|
|
711
|
+
* Present when a loop ended `exhausted` because its `noProgress` detector
|
|
712
|
+
* tripped: the evidence that the last `window` iterations reached no state
|
|
713
|
+
* the run had not already seen. Lets a supervisor tell "stalled, re-brief it"
|
|
714
|
+
* from "ran out of runway mid-progress" without parsing the summary.
|
|
715
|
+
*/
|
|
716
|
+
stall?: StallReport;
|
|
598
717
|
/**
|
|
599
718
|
* Structured feedback asking an earlier unit of work for another pass, and the
|
|
600
719
|
* single channel for it. When `revision.target` is set, the enclosing `dag`
|
|
@@ -747,6 +866,17 @@ interface LoopConfig {
|
|
|
747
866
|
stopOn?: ConditionInput;
|
|
748
867
|
/** Iteration cap. Reached without passing => `exhausted`. */
|
|
749
868
|
max?: number;
|
|
869
|
+
/**
|
|
870
|
+
* The third hard stop, alongside `max` and `budget`: end the loop `exhausted`
|
|
871
|
+
* when this many consecutive iterations make no observable progress — no
|
|
872
|
+
* workspace state the run has not already visited, no custom `signal` value
|
|
873
|
+
* not already seen, no gate confidence beating its previous best. A bare
|
|
874
|
+
* number is the window (`3` ⇒ three flat iterations); pass a `NoProgressConfig`
|
|
875
|
+
* for the full knobs. Off by default: a polling loop legitimately makes no
|
|
876
|
+
* progress until the outside world changes, so this is opt-in like `commit`.
|
|
877
|
+
* The stalled outcome carries the evidence as `Outcome.stall`.
|
|
878
|
+
*/
|
|
879
|
+
noProgress?: NoProgressInput;
|
|
750
880
|
/**
|
|
751
881
|
* Runs when `until` is met. If it returns `pass`, the loop completes.
|
|
752
882
|
* Any other status re-enters the loop — this is the "review fails, run the
|
|
@@ -881,6 +1011,12 @@ type LoopEvent = {
|
|
|
881
1011
|
path: string[];
|
|
882
1012
|
outcome: Outcome;
|
|
883
1013
|
iterations: number;
|
|
1014
|
+
} | {
|
|
1015
|
+
kind: 'loop:stall';
|
|
1016
|
+
ts: number;
|
|
1017
|
+
path: string[];
|
|
1018
|
+
iteration: number;
|
|
1019
|
+
report: StallReport;
|
|
884
1020
|
} | {
|
|
885
1021
|
kind: 'limit:wait';
|
|
886
1022
|
ts: number;
|
|
@@ -976,4 +1112,4 @@ type LoopEvent = {
|
|
|
976
1112
|
code: string;
|
|
977
1113
|
};
|
|
978
1114
|
|
|
979
|
-
export { type
|
|
1115
|
+
export { type NoProgressInput as $, type AgentDef as A, type BudgetConfig as B, type ConditionInput as C, type DagConfig as D, type Environment as E, type FeedbackFinding as F, type GraphPosition as G, type CommitJobConfig as H, type ConditionResult as I, type Job as J, type DagNode as K, type LoopConfig as L, type EngineStreamEvent as M, type ForgeOpts as N, type Outcome as O, GhForge as P, type GroundConfig as Q, type RevisionRerun as R, type LogLevel as S, LoopError as T, type Usage as U, type LoopErrorCode as V, type Workspace as W, type MergeOptions as X, MockForge as Y, type MockForgeOptions as Z, type NoProgressConfig as _, type FeedbackDecision as a, type OutcomeStatus as a0, type PrInput as a1, type PrPatch as a2, type PrRef as a3, type ProgressSample as a4, ProgressTracker as a5, type RawPredicate as a6, type RetryPolicy as a7, SUBAGENT_TOOLS as a8, type Skill as a9, type StallReport as aa, agentContract as ab, agentJob as ac, buildChecksArgs as ad, buildCreateArgs as ae, buildEditArgs as af, buildMergeArgs as ag, buildViewArgs as ah, commitJob as ai, defineAgent as aj, defineSkill as ak, fnJob as al, fromFile as am, isEngine as an, isEnvironment as ao, isForge as ap, resolveNoProgress as aq, resolveSystem as ar, type FeedbackSeverity as b, type FeedbackActionSeverity as c, type JobContext as d, type RevisionRequest as e, type JobMeta as f, type EngineRef as g, type Condition as h, type EngineOptions as i, type Engine as j, type EngineName as k, type AgentRequest as l, type EngineEventSink as m, type AgentResult as n, type EnvHandle as o, type LoopEvent as p, type Forge as q, type LimitPolicy as r, type AgentContractSummary as s, type AgentFailureMode as t, type AgentHumanGate as u, type AgentJobConfig as v, type AgentOutputContract as w, type AgentSkillRef as x, type AgentTier as y, Budget as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loops-adk/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Jonny Neill",
|
|
6
6
|
"description": "Run an agent in a convergence loop with an honest done-gate. A small, nestable loop and DAG primitive: deterministic plus agent-judge conditions, git as memory, review-restart, budgets, and a live TUI.",
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"bench:context:dry": "BENCH_DRY=1 BENCH_CB_GROUPS=bench/contextbench/groups.dry.json tsx bench/swecontextbench.ts",
|
|
75
75
|
"bench:mechanism": "tsx bench/mechanism.ts",
|
|
76
76
|
"example:poll": "tsx src/index.ts run examples/simple-poll.loop.ts --no-tui",
|
|
77
|
+
"example:stall": "tsx src/index.ts run examples/stall-demo.loop.ts --no-tui",
|
|
77
78
|
"example:gate": "tsx src/index.ts run examples/confidence-gate.loop.ts",
|
|
78
79
|
"prepack": "npm run build",
|
|
79
80
|
"prepublishOnly": "npm run typecheck"
|