aibroker 0.36.0 → 0.37.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.
@@ -0,0 +1,100 @@
1
+ /**
2
+ * daemon/dialogs.ts — the modal nobody is there to click.
3
+ *
4
+ * WHY THIS EXISTS. Everything else that watches a managed session watches its
5
+ * TERMINAL: the pane's text, the transcript, the manager's own history. A macOS
6
+ * modal belongs to a different process and appears in none of them, so a
7
+ * session can sit blocked behind one for hours while every signal reads
8
+ * healthy — working, goal armed, screen unchanged for a plausible reason. An
9
+ * operator discovered this the only way it can be discovered without this file:
10
+ * by finding the dialog by hand and clicking it a hundred times.
11
+ *
12
+ * WHAT IT WILL AND WILL NOT PRESS. Only prompts of the "may this app do the
13
+ * obvious thing it was just asked to do" kind, from a fixed list of system
14
+ * agents, with a fixed list of button titles. Anything asking for a credential
15
+ * is excluded by name and by construction: SecurityAgent is not on the list,
16
+ * and no button title here is one that a password sheet offers. The rule is
17
+ * that this may only re-consent to something the operator already set in
18
+ * motion; it may never grant something new.
19
+ */
20
+ import { runAppleScript } from "../adapters/iterm/core.js";
21
+ import { log } from "../core/log.js";
22
+ /**
23
+ * The agents allowed to have their dialogs answered.
24
+ *
25
+ * Short and closed on purpose. Each is a system process whose entire job is
26
+ * asking permission for an action a user already initiated — opening an app
27
+ * that was just built, letting one app drive another that it has been driving
28
+ * all evening. SecurityAgent is deliberately ABSENT: that is the one that asks
29
+ * for passwords and disk access, and nothing should ever answer it but a
30
+ * person.
31
+ */
32
+ const ANSWERABLE = ["CoreServicesUIAgent", "UserNotificationCenter"];
33
+ /**
34
+ * Buttons that mean "yes, as already intended".
35
+ *
36
+ * Matched exactly, never as a substring — "Don't Allow" contains "Allow", and a
37
+ * substring match would turn a refusal into consent. Order matters: the first
38
+ * title found on the dialog is the one pressed.
39
+ */
40
+ const SAFE_BUTTONS = ["Open", "Allow", "OK"];
41
+ const LIST_SCRIPT = `
42
+ tell application "System Events"
43
+ set out to ""
44
+ repeat with pname in {${ANSWERABLE.map((p) => `"${p}"`).join(", ")}}
45
+ try
46
+ tell process pname
47
+ repeat with w in windows
48
+ set btns to ""
49
+ try
50
+ repeat with b in buttons of w
51
+ set btns to btns & (title of b) & "~"
52
+ end repeat
53
+ end try
54
+ set out to out & (pname as string) & "|" & (name of w) & "|" & btns & linefeed
55
+ end repeat
56
+ end tell
57
+ end try
58
+ end repeat
59
+ return out
60
+ end tell`;
61
+ /** Every dialog currently on screen that belongs to an answerable agent. */
62
+ export function listDialogs() {
63
+ const raw = runAppleScript(LIST_SCRIPT, 10_000);
64
+ if (!raw)
65
+ return [];
66
+ return raw
67
+ .split("\n")
68
+ .map((line) => line.trim())
69
+ .filter(Boolean)
70
+ .map((line) => {
71
+ const [process, title, btns] = line.split("|");
72
+ return {
73
+ process: process ?? "",
74
+ title: title ?? "",
75
+ buttons: (btns ?? "").split("~").map((b) => b.trim()).filter(Boolean),
76
+ };
77
+ })
78
+ .filter((d) => ANSWERABLE.includes(d.process));
79
+ }
80
+ /**
81
+ * Answer one dialog, and say which button was pressed.
82
+ *
83
+ * Returns null when nothing was pressed, which includes the case of a dialog
84
+ * offering none of the safe titles — an unrecognised prompt is left alone and
85
+ * reported rather than guessed at, because the whole value of a closed list is
86
+ * lost the moment something falls back to "press the default".
87
+ */
88
+ export function answerDialog(d) {
89
+ const button = SAFE_BUTTONS.find((b) => d.buttons.includes(b));
90
+ if (!button)
91
+ return null;
92
+ const script = `tell application "System Events" to tell process "${d.process}" to click button "${button}" of window 1`;
93
+ const ok = runAppleScript(script, 10_000);
94
+ if (ok === null) {
95
+ log(`[dialogs] could not press "${button}" on ${d.process}`);
96
+ return null;
97
+ }
98
+ return button;
99
+ }
100
+ //# sourceMappingURL=dialogs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialogs.js","sourceRoot":"","sources":["../../src/daemon/dialogs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAErC;;;;;;;;;GASG;AACH,MAAM,UAAU,GAAG,CAAC,qBAAqB,EAAE,wBAAwB,CAAC,CAAC;AAErE;;;;;;GAMG;AACH,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AAS7C,MAAM,WAAW,GAAG;;;0BAGM,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;SAgB3D,CAAC;AAEV,4EAA4E;AAC5E,MAAM,UAAU,WAAW;IACzB,MAAM,GAAG,GAAG,cAAc,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAChD,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,OAAO,GAAG;SACP,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/C,OAAO;YACL,OAAO,EAAE,OAAO,IAAI,EAAE;YACtB,KAAK,EAAE,KAAK,IAAI,EAAE;YAClB,OAAO,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;SACtE,CAAC;IACJ,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,CAAa;IACxC,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,MAAM,GAAG,qDAAqD,CAAC,CAAC,OAAO,sBAAsB,MAAM,eAAe,CAAC;IACzH,MAAM,EAAE,GAAG,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;QAChB,GAAG,CAAC,8BAA8B,MAAM,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -36,6 +36,54 @@ export interface ManagedSession {
36
36
  lastChangeAt: number;
37
37
  lastHash: string;
38
38
  paused: boolean;
39
+ /**
40
+ * The file the session hands over in — a TEMPLATE, not a fixed path.
41
+ *
42
+ * `{date}`, `{yyyy}`, `{mm}` and `{dd}` are expanded when the file is used
43
+ * rather than when it is set, because a session that runs for days outlives
44
+ * the day it was started on. A literal date written into the path was right
45
+ * for one evening and then quietly wrong: the request kept naming yesterday's
46
+ * file, and the drift grows by a day every day.
47
+ */
48
+ handoverFile?: string;
49
+ /** The path actually asked for, so a date rolling over mid-episode cannot
50
+ * make the change check compare two different files. */
51
+ handoverAskedPath?: string;
52
+ /**
53
+ * Clear the session after the handover, rather than leaving it to compact.
54
+ * OFF unless asked for — see the rollover block for what changed this.
55
+ */
56
+ clearAfterHandover?: boolean;
57
+ /** When a handover was last obtained, so it is not demanded every tick. */
58
+ handoverDoneAt?: number;
59
+ /** The context reading when it was obtained, so "how much work since" is
60
+ * answerable — a handover ages by work done, not by the clock. */
61
+ handoverDoneK?: number;
62
+ /** When a handover was asked for, so a silent session can be given up on. */
63
+ handoverAskedAt?: number;
64
+ /** What that file looked like when asked, so "changed" is measured not claimed. */
65
+ handoverWas?: string;
66
+ /** When a clear was typed, so it is never typed twice WITHIN one rollover. */
67
+ clearTypedAt?: number;
68
+ /**
69
+ * When a clear was typed that has never been seen to land — ACROSS rollovers.
70
+ *
71
+ * The per-rollover guard was not enough and the gap was ugly: a session whose
72
+ * context stays high keeps qualifying for rollover, so each new attempt typed
73
+ * its own clear, and a session in a turn long enough to execute none of them
74
+ * accumulated a queue of them. They would then all fire in sequence, the
75
+ * first against the session they were meant for and the rest against whatever
76
+ * fresh session had started since — which is the exact "wipe the session that
77
+ * just started" failure the single-clear rule existed to prevent, reached by
78
+ * going around it rather than through it.
79
+ *
80
+ * So the invariant is stronger than "one clear per rollover": at most one
81
+ * unconfirmed clear per session, ever, and no new rollover may begin while
82
+ * one is outstanding.
83
+ */
84
+ clearPendingSince?: number;
85
+ /** Context before the clear, so "it landed" is measured against something. */
86
+ contextAtClear?: number;
39
87
  /** The pane, so the process and thence the checkout can be found again. */
40
88
  tty?: string;
41
89
  /** Screen work forbidden — the operator has the machine. Survives re-arming. */
@@ -47,6 +95,36 @@ export interface ManagedSession {
47
95
  handsWas?: boolean;
48
96
  startedAt: number;
49
97
  }
98
+ /**
99
+ * Is there text sitting unsent in the session's prompt?
100
+ *
101
+ * TYPING ON TOP OF IT DESTROYS IT. The manager pastes into the same input line
102
+ * a person types into, and it sends a backspace first to escape vi normal
103
+ * mode — so an objective armed over half-typed text eats a character of that
104
+ * text and then runs the two together as one prompt. The operator's sentence
105
+ * and the standing objective arrive merged and mangled, and neither does what
106
+ * it meant to.
107
+ *
108
+ * This never bit while nobody was at the keyboard, which is exactly the kind of
109
+ * assumption that holds until an operator sits down at seven in the morning and
110
+ * starts a sentence.
111
+ *
112
+ * The live input line is the one enclosed by the terminal's rules at the foot
113
+ * of the pane, not the `❯` lines further up — those are scrollback, commands
114
+ * that already ran. So the rule immediately above it is what identifies it.
115
+ */
116
+ export declare function promptHasUnsentText(content: string): boolean;
117
+ /**
118
+ * Expand the date tokens in a handover path, against the clock right now.
119
+ *
120
+ * Deliberately resolved at the moment of use. A managed session is meant to
121
+ * outlive the day it started on, so any date fixed at the moment the path was
122
+ * SET is a date that will be wrong by morning — and wrong in the quietest way,
123
+ * since the file it names still exists and still opens.
124
+ *
125
+ * Exported for the tests, which is also where the accepted tokens are pinned.
126
+ */
127
+ export declare function resolveHandoverPath(template: string, at?: Date): string;
50
128
  /**
51
129
  * Resolve a session by whatever the caller knows — its id, or its name.
52
130
  *
@@ -57,6 +135,20 @@ export declare function resolveSession(idOrName: string): {
57
135
  sessionId: string;
58
136
  name: string;
59
137
  } | null;
138
+ /**
139
+ * Collapse text to a single line, for anything about to be TYPED at a prompt.
140
+ *
141
+ * At a prompt a newline is the submit key, so a multi-line objective does not
142
+ * arrive as a long goal — it arrives as a short one, followed by its own
143
+ * remainder as a second, contextless prompt, and the session acts on both. The
144
+ * damage is silent: what was sent looks right in the state file and wrong only
145
+ * on screen.
146
+ *
147
+ * Applied at the point text becomes keystrokes rather than at each point text
148
+ * is set, so it covers every route in — shell heredoc, MCP call, appended
149
+ * text, operator notes — including the ones added later.
150
+ */
151
+ export declare function oneLine(text: string): string;
60
152
  export declare function startManagerLoop(): void;
61
153
  export interface ManageResult {
62
154
  ok: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"manage.d.ts","sourceRoot":"","sources":["../../src/daemon/manage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAyDH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4DAA4D;IAC5D,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;kFAC8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AA6KD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAY3F;AAyaD,wBAAgB,gBAAgB,IAAI,IAAI,CASvC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAoSjG"}
1
+ {"version":3,"file":"manage.d.ts","sourceRoot":"","sources":["../../src/daemon/manage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAmLH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4DAA4D;IAC5D,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;6DACyD;IACzD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;uEACmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;;;;;OAeG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;kFAC8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAyLD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAa5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,GAAE,IAAiB,GAAG,MAAM,CAUnF;AA6KD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAY3F;AA8PD;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAwjBD,wBAAgB,gBAAgB,IAAI,IAAI,CASvC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA8ZjG"}