@qloo/qloo-harness 0.1.18

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,666 @@
1
+ import { createRequire as __qlooCreateRequire } from "node:module";
2
+ const require = __qlooCreateRequire(import.meta.url);
3
+
4
+ // apps/qloo-harness/dist/runtime/explore-policy.js
5
+ import { findGuidedGoal, guidedGoalLabel, QLOO_GUIDED_GOALS, qlooNextActions, qlooStarterIdeaLines } from "../guided-journey.js";
6
+ import { getQlooProfileDefinition } from "../profiles.js";
7
+ import { applyResolutionChoices, formatQlooResolutionCandidateLabels, paginateQlooResult, parseQlooRawPage, resolutionCandidates } from "../qloo-presentation.js";
8
+ import { QLOO_NO_GUIDED_START_ENVIRONMENT_VARIABLE } from "../setup.js";
9
+ import { isBlockedPiInteractiveCommand } from "./pi-command-policy.js";
10
+ import { renderQlooHeader } from "./qloo-header.js";
11
+ var QLOO_WORKSPACE_TOOL_NAMES = [
12
+ ...getQlooProfileDefinition("build").workspaceTools
13
+ ];
14
+ var QLOO_SESSION_STATE_ENTRY_TYPE = "qloo.workflow-state";
15
+ var QLOO_SESSION_STATE_SCHEMA_VERSION = "1.4";
16
+ var QLOO_NEXT_WIDGET_KEY = "qloo-next-actions";
17
+ var QLOO_START_WIDGET_KEY = "qloo-start-ideas";
18
+ var AUTOMATIC_DIAGNOSTIC_CODES = /* @__PURE__ */ new Set([
19
+ "QLOO_AUTH",
20
+ "QLOO_CONNECT",
21
+ "QLOO_DNS",
22
+ "QLOO_TIMEOUT",
23
+ "QLOO_TLS",
24
+ "RESOLVER_CONNECT",
25
+ "RESOLVER_TIMEOUT"
26
+ ]);
27
+ var reservedBuiltInTools = /* @__PURE__ */ new Set([
28
+ ...QLOO_WORKSPACE_TOOL_NAMES,
29
+ "grep",
30
+ "find",
31
+ "ls"
32
+ ]);
33
+ function buildExploreToolNames(tools) {
34
+ return buildProfileToolNames("explore", tools);
35
+ }
36
+ function buildProfileToolNames(profile, tools) {
37
+ validateExploreTools(tools);
38
+ return [
39
+ ...getQlooProfileDefinition(profile).workspaceTools,
40
+ ...tools.map(({ name }) => name)
41
+ ];
42
+ }
43
+ function validateExploreTools(tools) {
44
+ const names = /* @__PURE__ */ new Set();
45
+ for (const tool of tools) {
46
+ const name = tool.name.trim();
47
+ if (name.length === 0) {
48
+ throw new Error("Qloo tool names must not be empty");
49
+ }
50
+ if (reservedBuiltInTools.has(name)) {
51
+ throw new Error(`Qloo tool name conflicts with a reserved Pi built-in: ${name}`);
52
+ }
53
+ if (names.has(name)) {
54
+ throw new Error(`Duplicate Qloo tool name: ${name}`);
55
+ }
56
+ names.add(name);
57
+ }
58
+ }
59
+ function safeApprovalDetail(toolName, input) {
60
+ const candidate = toolName === "bash" ? input.command : input.path ?? input.file_path;
61
+ if (typeof candidate !== "string" || candidate.trim().length === 0)
62
+ return toolName;
63
+ const sanitized = candidate.replace(/[\u0000-\u001f\u007f]/gu, " ").trim();
64
+ return sanitized.length > 240 ? `${sanitized.slice(0, 240)}\u2026` : sanitized;
65
+ }
66
+ function stableToolSignature(toolName, input) {
67
+ const normalize = (value) => {
68
+ if (Array.isArray(value))
69
+ return value.map(normalize);
70
+ if (value === null || typeof value !== "object")
71
+ return value;
72
+ return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, normalize(item)]));
73
+ };
74
+ return `${toolName}:${JSON.stringify(normalize(input))}`;
75
+ }
76
+ var sensitiveResultKey = /(?:api[_-]?key|authorization|credential|password|secret|token)/iu;
77
+ function redactSensitiveResult(value, depth = 0) {
78
+ if (depth >= 8)
79
+ return "[detail omitted]";
80
+ if (Array.isArray(value))
81
+ return value.map((item) => redactSensitiveResult(item, depth + 1));
82
+ if (value === null || typeof value !== "object")
83
+ return value;
84
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [
85
+ key,
86
+ sensitiveResultKey.test(key) ? "[redacted]" : redactSensitiveResult(item, depth + 1)
87
+ ]));
88
+ }
89
+ function boundedJsonValue(value, maximumBytes) {
90
+ try {
91
+ const serialized = JSON.stringify(redactSensitiveResult(value));
92
+ if (Buffer.byteLength(serialized) > maximumBytes)
93
+ return void 0;
94
+ return JSON.parse(serialized);
95
+ } catch {
96
+ return void 0;
97
+ }
98
+ }
99
+ function boundedWorkflowInput(input) {
100
+ return boundedJsonValue(input, 16384);
101
+ }
102
+ function branchHasConversation(entries) {
103
+ return entries.some((entry) => {
104
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry))
105
+ return false;
106
+ const record = entry;
107
+ return record.type === "message" || record.type === "custom_message" || record.type === "compaction";
108
+ });
109
+ }
110
+ function compactDoctorRecovery(report, recovery) {
111
+ const record = report !== null && typeof report === "object" && !Array.isArray(report) ? report : void 0;
112
+ const checks = Array.isArray(record?.checks) ? record.checks.filter((check) => check !== null && typeof check === "object" && !Array.isArray(check)).filter((check) => check.status !== "ok").slice(0, 3).map((check) => typeof check.message === "string" ? check.message : void 0).filter((message) => Boolean(message)) : [];
113
+ const next = checks[0] ?? (typeof recovery === "string" && recovery.trim() ? recovery : "Run /doctor network before retrying.");
114
+ return [
115
+ "Qloo automatically checked the failing connection.",
116
+ `Diagnosis: ${String(record?.status ?? "unavailable")}`,
117
+ `Next step: ${next}`,
118
+ "Use /doctor network for the full report or /trace for the recorded failure."
119
+ ].join("\n");
120
+ }
121
+ function requestPreviews(workflow) {
122
+ const provenance = workflow.provenance;
123
+ if (provenance === null || typeof provenance !== "object" || Array.isArray(provenance)) {
124
+ return void 0;
125
+ }
126
+ return provenance.requests;
127
+ }
128
+ function createHarnessPolicyExtension(profile, allowedToolNames, runtimeInfo, commandActions = {}) {
129
+ return {
130
+ name: `qloo-${profile}-policy`,
131
+ hidden: true,
132
+ factory(pi) {
133
+ let autocompletePolicyInstalled = false;
134
+ let starterIdeasShown = false;
135
+ let diagnosedThisTurn = false;
136
+ let toolCallsThisTurn = 0;
137
+ const identicalCallsThisTurn = /* @__PURE__ */ new Map();
138
+ let lastQlooResult;
139
+ let lastQlooRawResult;
140
+ let lastQlooInput;
141
+ let lastQlooToolName;
142
+ let rawPage = 1;
143
+ let activeProfile = profile;
144
+ const customToolNames = [...allowedToolNames].filter((name) => !reservedBuiltInTools.has(name));
145
+ const activeDefinition = () => getQlooProfileDefinition(activeProfile);
146
+ const activeAllowedToolNames = () => /* @__PURE__ */ new Set([
147
+ ...activeDefinition().workspaceTools,
148
+ ...customToolNames
149
+ ]);
150
+ const activateProfile = (nextProfile, context, reason = "resume") => {
151
+ activeProfile = nextProfile;
152
+ pi.setActiveTools([...activeAllowedToolNames()]);
153
+ const status = `Qloo \xB7 ${nextProfile}`;
154
+ context?.ui?.setStatus?.("qloo-profile", context.ui.theme?.fg("accent", status) ?? status);
155
+ context?.ui?.notify(reason === "resume" ? `Qloo resumed in the read-only ${nextProfile} profile; prior build authority was not restored.` : `Qloo switched to the ${nextProfile} profile for this guided path.`, reason === "resume" ? "warning" : "info");
156
+ };
157
+ const restoreLastQlooResult = (context) => {
158
+ const entries = context.sessionManager?.getBranch() ?? [];
159
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
160
+ const entry = entries[index];
161
+ if (entry === null || typeof entry !== "object")
162
+ continue;
163
+ const record = entry;
164
+ if (record.type !== "custom" || record.customType !== QLOO_SESSION_STATE_ENTRY_TYPE)
165
+ continue;
166
+ const data = record.data;
167
+ if (data === null || typeof data !== "object" || Array.isArray(data))
168
+ continue;
169
+ const workflow = data.workflow;
170
+ if (workflow !== null && typeof workflow === "object" && !Array.isArray(workflow)) {
171
+ lastQlooResult = workflow;
172
+ const restoredInput = lastQlooResult.input;
173
+ if (restoredInput !== null && typeof restoredInput === "object" && !Array.isArray(restoredInput)) {
174
+ lastQlooInput = restoredInput;
175
+ }
176
+ if (typeof lastQlooResult.tool_name === "string") {
177
+ lastQlooToolName = lastQlooResult.tool_name;
178
+ }
179
+ }
180
+ return;
181
+ }
182
+ };
183
+ const notifyJson = (context, label, value, maximumCharacters = 4e3) => {
184
+ const serialized = JSON.stringify(redactSensitiveResult(value), null, 2);
185
+ const bounded = serialized.length > maximumCharacters ? `${serialized.slice(0, maximumCharacters)}
186
+ \u2026` : serialized;
187
+ context.ui.notify(`${label}
188
+ ${bounded}`);
189
+ };
190
+ const setNextWidget = (context) => {
191
+ if (context?.mode !== "tui" || !context.ui?.setWidget)
192
+ return;
193
+ context.ui.setWidget(QLOO_NEXT_WIDGET_KEY, ["Next: /next choose a follow-up \xB7 /why interpretation \xB7 /request API request"], { placement: "belowEditor" });
194
+ };
195
+ const recordQlooResult = (toolName, input, details, context) => {
196
+ const boundedInput = boundedWorkflowInput(input);
197
+ const boundedResolution = boundedJsonValue(details.resolution, 24576);
198
+ const boundedWarnings = boundedJsonValue(details.warnings, 4096);
199
+ const boundedProvenance = boundedJsonValue(details.provenance, 24576);
200
+ const boundedExecution = boundedJsonValue(details.execution, 4096);
201
+ lastQlooRawResult = details;
202
+ rawPage = 1;
203
+ lastQlooInput = boundedInput;
204
+ lastQlooToolName = toolName;
205
+ lastQlooResult = {
206
+ schema_version: details.schema_version,
207
+ operation: details.operation,
208
+ tool_name: toolName,
209
+ ...boundedInput ? { input: boundedInput } : {},
210
+ status: details.status,
211
+ result_count: details.result_count,
212
+ query_intent: details.query_intent,
213
+ error: details.error,
214
+ ...boundedResolution !== void 0 ? { resolution: boundedResolution } : {},
215
+ ...boundedWarnings !== void 0 ? { warnings: boundedWarnings } : {},
216
+ ...boundedProvenance !== void 0 ? { provenance: boundedProvenance } : {},
217
+ ...boundedExecution !== void 0 ? { execution: boundedExecution } : {}
218
+ };
219
+ pi.appendEntry(QLOO_SESSION_STATE_ENTRY_TYPE, {
220
+ schema_version: QLOO_SESSION_STATE_SCHEMA_VERSION,
221
+ profile: activeProfile,
222
+ workflow: lastQlooResult
223
+ });
224
+ setNextWidget(context);
225
+ };
226
+ const runDoctorAction = async (context, network, compact = false, recovery) => {
227
+ if (!commandActions.doctor) {
228
+ context.ui.notify("In-chat diagnostics are unavailable in this embedded runtime. Run qloo doctor --network.", "warning");
229
+ return;
230
+ }
231
+ context.ui.notify(network ? "Running bounded Qloo network diagnostics\u2026" : "Running Qloo diagnostics\u2026");
232
+ try {
233
+ const report = await commandActions.doctor(network);
234
+ if (compact) {
235
+ context.ui.notify(compactDoctorRecovery(report, recovery));
236
+ } else {
237
+ notifyJson(context, "Qloo doctor", report, 8e3);
238
+ }
239
+ } catch {
240
+ context.ui.notify("Qloo diagnostics could not complete. Run qloo doctor --network from the terminal.", "error");
241
+ }
242
+ };
243
+ const showRequestAction = (context) => {
244
+ if (!lastQlooResult) {
245
+ context.ui.notify("No Qloo workflow request has been recorded in this session yet.", "warning");
246
+ return;
247
+ }
248
+ const requests = requestPreviews(lastQlooResult);
249
+ if (!Array.isArray(requests) || requests.length === 0) {
250
+ context.ui.notify("This transport did not expose a safe request preview. Use /why and /sources to inspect the normalized workflow.", "warning");
251
+ return;
252
+ }
253
+ notifyJson(context, "Latest Qloo API request", requests, 12e3);
254
+ };
255
+ const retryLastWorkflow = async (context) => {
256
+ if (!lastQlooToolName || !lastQlooInput) {
257
+ context.ui.notify("No replayable Qloo workflow input has been recorded in this session yet.", "warning");
258
+ return;
259
+ }
260
+ if (!commandActions.retry) {
261
+ context.ui.notify("Retry is unavailable in this embedded runtime.", "warning");
262
+ return;
263
+ }
264
+ context.ui.notify(`Retrying ${lastQlooToolName} once on ${runtimeInfo?.transport ?? "the selected transport"}\u2026`);
265
+ try {
266
+ const details = await commandActions.retry(lastQlooToolName, lastQlooInput);
267
+ recordQlooResult(lastQlooToolName, lastQlooInput, details, context);
268
+ context.ui.notify([
269
+ `${lastQlooToolName} retry completed.`,
270
+ `Status: ${String(details.status ?? "unknown")}`,
271
+ `Results: ${String(details.result_count ?? "unknown")}`,
272
+ "Use /next, /raw, /why, /request, /sources, or /trace to inspect it."
273
+ ].join("\n"));
274
+ } catch {
275
+ context.ui.notify("The Qloo retry failed inside the harness. Use /trace or qloo doctor --network.", "error");
276
+ }
277
+ };
278
+ const activateGuidedGoal = (goal, context) => {
279
+ if (goal.profile && goal.profile !== activeProfile) {
280
+ activateProfile(goal.profile, context, "journey");
281
+ }
282
+ };
283
+ const showGuidedStart = async (context) => {
284
+ if (context.mode !== "tui" || !context.hasUI) {
285
+ context.ui.notify("Guided start is available in the interactive terminal.", "warning");
286
+ return;
287
+ }
288
+ context.ui.setWidget(QLOO_START_WIDGET_KEY, void 0);
289
+ const selected = await context.ui.select("What would you like to do with Qloo?", QLOO_GUIDED_GOALS.map(guidedGoalLabel));
290
+ if (!selected)
291
+ return;
292
+ const goal = findGuidedGoal(selected);
293
+ if (!goal)
294
+ return;
295
+ activateGuidedGoal(goal, context);
296
+ if (goal.id === "diagnose") {
297
+ await runDoctorAction(context, true);
298
+ return;
299
+ }
300
+ if (goal.id === "blank" || goal.starters.length === 0)
301
+ return;
302
+ const ownPrompt = "Write my own prompt";
303
+ const starterLabel = await context.ui.select(goal.label, [...goal.starters.map(({ label }) => label), ownPrompt]);
304
+ if (!starterLabel || starterLabel === ownPrompt)
305
+ return;
306
+ const starter = goal.starters.find(({ label }) => label === starterLabel);
307
+ if (!starter)
308
+ return;
309
+ context.ui.setEditorText(starter.prompt);
310
+ context.ui.notify("Starter added to the editor. Replace the bracketed details, then press Enter.");
311
+ };
312
+ const showStarterIdeas = (context) => {
313
+ if (context.mode !== "tui" || !context.hasUI)
314
+ return;
315
+ context.ui.setWidget(QLOO_START_WIDGET_KEY, (_tui, theme) => ({
316
+ render(width) {
317
+ const lines = qlooStarterIdeaLines(width);
318
+ return lines.map((line, index) => {
319
+ if (index === 0)
320
+ return theme.bold(theme.fg("accent", line));
321
+ if (index === lines.length - 1)
322
+ return theme.fg("dim", line);
323
+ return theme.fg("muted", line);
324
+ });
325
+ },
326
+ invalidate() {
327
+ }
328
+ }));
329
+ };
330
+ const showNextActions = async (context) => {
331
+ if (!lastQlooResult) {
332
+ context.ui.notify("No Qloo result is available yet. Use /start for guided examples.", "warning");
333
+ return;
334
+ }
335
+ const actions = qlooNextActions(lastQlooResult);
336
+ const selected = await context.ui.select("What would you like to do next?", actions.map(({ label }) => label));
337
+ const action = actions.find(({ label }) => label === selected);
338
+ if (!action)
339
+ return;
340
+ if (action.kind === "prompt") {
341
+ if (action.profile && action.profile !== activeProfile) {
342
+ activateProfile(action.profile, context, "journey");
343
+ }
344
+ context.ui.setEditorText(action.value);
345
+ return;
346
+ }
347
+ if (action.value === "request")
348
+ showRequestAction(context);
349
+ else if (action.value === "why") {
350
+ notifyJson(context, "Latest Qloo query intent", lastQlooResult.query_intent);
351
+ } else if (action.value === "doctor network") {
352
+ await runDoctorAction(context, true);
353
+ } else if (action.value === "retry") {
354
+ await retryLastWorkflow(context);
355
+ }
356
+ };
357
+ const maybeRunAutomaticDiagnosis = async (details, context) => {
358
+ if (diagnosedThisTurn || !commandActions.doctor || details.status !== "error")
359
+ return;
360
+ const error = details.error;
361
+ if (error === null || typeof error !== "object" || Array.isArray(error))
362
+ return;
363
+ const errorRecord = error;
364
+ if (typeof errorRecord.code !== "string" || !AUTOMATIC_DIAGNOSTIC_CODES.has(errorRecord.code)) {
365
+ return;
366
+ }
367
+ diagnosedThisTurn = true;
368
+ await runDoctorAction(context, true, true, errorRecord.recovery);
369
+ };
370
+ pi.registerCommand("start", {
371
+ description: "Choose a guided Qloo goal and editable starter prompt",
372
+ handler: async (_args, context) => showGuidedStart(context)
373
+ });
374
+ pi.registerCommand("status", {
375
+ description: "Show Qloo profile, transport, model, workspace, and context usage",
376
+ handler: async (_args, context) => {
377
+ const usage = context.getContextUsage();
378
+ const model = context.model ? `${context.model.provider}/${context.model.id}` : "unavailable";
379
+ context.ui.notify([
380
+ "Qloo status",
381
+ `Profile: ${activeProfile}`,
382
+ `Transport: ${runtimeInfo ? `${runtimeInfo.transport} (${runtimeInfo.transportName})` : "not declared"}`,
383
+ `Resolution: ${runtimeInfo?.resolution ? `${runtimeInfo.resolution.name} (${runtimeInfo.resolution.tag_strategy}${runtimeInfo.resolution.uses_model ? ", model-assisted" : ""})` : "not declared"}`,
384
+ `Fallback: ${runtimeInfo?.fallback ?? "none"}`,
385
+ `Model: ${model}`,
386
+ `Workspace: ${context.cwd}`,
387
+ `Context: ${usage?.tokens ?? "unknown"}/${usage?.contextWindow ?? "unknown"} tokens${usage?.percent === null || usage?.percent === void 0 ? "" : ` (${usage.percent.toFixed(1)}%)`}`,
388
+ `Turn budget: ${activeDefinition().maxToolCallsPerTurn} calls; ${activeDefinition().maxIdenticalToolCallsPerTurn} identical calls`
389
+ ].join("\n"));
390
+ }
391
+ });
392
+ pi.registerCommand("why", {
393
+ description: "Show the normalized intent for the latest Qloo workflow",
394
+ handler: async (_args, context) => {
395
+ if (!lastQlooResult?.query_intent) {
396
+ context.ui.notify("No Qloo workflow intent has been recorded in this session yet.", "warning");
397
+ return;
398
+ }
399
+ notifyJson(context, "Latest Qloo query intent", lastQlooResult.query_intent);
400
+ }
401
+ });
402
+ pi.registerCommand("sources", {
403
+ description: "Show contract and API provenance for the latest Qloo workflow",
404
+ handler: async (_args, context) => {
405
+ if (!lastQlooResult) {
406
+ context.ui.notify("No Qloo workflow provenance has been recorded in this session yet.", "warning");
407
+ return;
408
+ }
409
+ notifyJson(context, "Latest Qloo provenance", {
410
+ schema_version: lastQlooResult.schema_version,
411
+ operation: lastQlooResult.operation,
412
+ resolution: lastQlooResult.resolution,
413
+ warnings: lastQlooResult.warnings,
414
+ provenance: lastQlooResult.provenance
415
+ });
416
+ }
417
+ });
418
+ pi.registerCommand("request", {
419
+ description: "Show the safe API request preview for the latest Qloo workflow",
420
+ handler: async (_args, context) => showRequestAction(context)
421
+ });
422
+ pi.registerCommand("trace", {
423
+ description: "Show safe execution metadata for the latest Qloo workflow",
424
+ handler: async (_args, context) => {
425
+ if (!lastQlooResult) {
426
+ context.ui.notify("No Qloo workflow execution has been recorded in this session yet.", "warning");
427
+ return;
428
+ }
429
+ notifyJson(context, "Latest Qloo execution", {
430
+ operation: lastQlooResult.operation,
431
+ status: lastQlooResult.status,
432
+ result_count: lastQlooResult.result_count,
433
+ error: lastQlooResult.error,
434
+ execution: lastQlooResult.execution
435
+ });
436
+ }
437
+ });
438
+ pi.registerCommand("raw", {
439
+ description: "Show a bounded result page; pass a page number, next, or prev",
440
+ handler: async (args, context) => {
441
+ if (!lastQlooRawResult) {
442
+ context.ui.notify(lastQlooResult ? "The latest raw result is not duplicated across resume. Use /retry to execute the recorded input again." : "No Qloo workflow result has been recorded in this session yet.", "warning");
443
+ return;
444
+ }
445
+ const requestedPage = parseQlooRawPage(args, rawPage);
446
+ if (requestedPage === void 0) {
447
+ context.ui.notify("Usage: /raw [page|next|prev]", "warning");
448
+ return;
449
+ }
450
+ const page = paginateQlooResult(lastQlooRawResult, requestedPage);
451
+ const pageInfo = page.page;
452
+ if (pageInfo !== null && typeof pageInfo === "object" && !Array.isArray(pageInfo)) {
453
+ const normalizedPage = pageInfo.number;
454
+ if (typeof normalizedPage === "number")
455
+ rawPage = normalizedPage;
456
+ }
457
+ notifyJson(context, `Latest normalized Qloo result \xB7 page ${rawPage}`, redactSensitiveResult(page), 12e3);
458
+ }
459
+ });
460
+ pi.registerCommand("next", {
461
+ description: "Choose a useful follow-up for the latest Qloo workflow",
462
+ handler: async (_args, context) => showNextActions(context)
463
+ });
464
+ pi.registerCommand("doctor", {
465
+ description: "Run static Qloo diagnostics; pass network for one active API probe",
466
+ handler: async (args, context) => {
467
+ const value = args.trim();
468
+ if (value !== "" && value !== "network" && value !== "--network") {
469
+ context.ui.notify("Usage: /doctor [network]", "warning");
470
+ return;
471
+ }
472
+ const network = value !== "";
473
+ await runDoctorAction(context, network);
474
+ }
475
+ });
476
+ pi.registerCommand("retry", {
477
+ description: "Replay the latest recorded Qloo workflow input once on the same transport",
478
+ handler: async (args, context) => {
479
+ if (args.trim().length > 0) {
480
+ context.ui.notify("Usage: /retry", "warning");
481
+ return;
482
+ }
483
+ await retryLastWorkflow(context);
484
+ }
485
+ });
486
+ pi.on("session_start", async (event, context) => {
487
+ if (activeProfile === "build" && event.reason !== "startup") {
488
+ activateProfile("plan", context);
489
+ }
490
+ restoreLastQlooResult(context);
491
+ if (context.mode !== "tui")
492
+ return;
493
+ context.ui.setTitle("Qloo");
494
+ context.ui.setHeader((_tui, theme) => ({
495
+ render: (width) => renderQlooHeader(theme, width, activeProfile),
496
+ invalidate() {
497
+ }
498
+ }));
499
+ context.ui.setStatus("qloo-profile", context.ui.theme.fg("accent", `Qloo \xB7 ${activeProfile}`));
500
+ if (!autocompletePolicyInstalled) {
501
+ context.ui.addAutocompleteProvider((current) => ({
502
+ ...current.triggerCharacters ? { triggerCharacters: [...current.triggerCharacters] } : {},
503
+ async getSuggestions(lines, cursorLine, cursorCol, options) {
504
+ const suggestions = await current.getSuggestions(lines, cursorLine, cursorCol, options);
505
+ if (!suggestions)
506
+ return null;
507
+ return {
508
+ ...suggestions,
509
+ items: suggestions.items.filter((item) => !isBlockedPiInteractiveCommand(item.value))
510
+ };
511
+ },
512
+ applyCompletion: (...args) => current.applyCompletion(...args),
513
+ ...current.shouldTriggerFileCompletion ? {
514
+ shouldTriggerFileCompletion: (...args) => current.shouldTriggerFileCompletion?.(...args) ?? false
515
+ } : {}
516
+ }));
517
+ autocompletePolicyInstalled = true;
518
+ }
519
+ const guidedStartEnabled = process.env[QLOO_NO_GUIDED_START_ENVIRONMENT_VARIABLE] !== "1";
520
+ const freshSession = (event.reason === "startup" || event.reason === "new") && !branchHasConversation(context.sessionManager.getBranch());
521
+ if (guidedStartEnabled && freshSession && !starterIdeasShown) {
522
+ starterIdeasShown = true;
523
+ showStarterIdeas(context);
524
+ }
525
+ });
526
+ pi.on("project_trust", () => ({ trusted: "no", remember: false }));
527
+ pi.on("turn_start", (_event, context) => {
528
+ diagnosedThisTurn = false;
529
+ toolCallsThisTurn = 0;
530
+ identicalCallsThisTurn.clear();
531
+ if (context.mode === "tui") {
532
+ context.ui.setWidget(QLOO_START_WIDGET_KEY, void 0);
533
+ context.ui.setWidget(QLOO_NEXT_WIDGET_KEY, void 0);
534
+ }
535
+ });
536
+ pi.on("tool_execution_start", (event, context) => {
537
+ if (!event.toolName.startsWith("qloo_"))
538
+ return;
539
+ context.ui.setWorkingMessage("Qloo \xB7 resolving and querying\u2026");
540
+ });
541
+ pi.on("tool_execution_end", (event, context) => {
542
+ if (!event.toolName.startsWith("qloo_"))
543
+ return;
544
+ context.ui.setWorkingMessage();
545
+ });
546
+ pi.on("tool_result", async (event, context) => {
547
+ if (!event.toolName.startsWith("qloo_") || event.toolName === "qloo_capabilities")
548
+ return;
549
+ if (event.details === null || typeof event.details !== "object" || Array.isArray(event.details))
550
+ return;
551
+ const details = event.details;
552
+ const issues = resolutionCandidates(details);
553
+ if (issues.length === 0 || !context.hasUI || !commandActions.retry) {
554
+ recordQlooResult(event.toolName, event.input, details, context);
555
+ await maybeRunAutomaticDiagnosis(details, context);
556
+ return;
557
+ }
558
+ const choices = [];
559
+ for (const issue of issues) {
560
+ const labels = formatQlooResolutionCandidateLabels(issue.candidates).map((label, index) => `${index + 1}. ${label}`);
561
+ const cancelLabel = "Cancel and ask me";
562
+ const selected = await context.ui.select(`Choose the Qloo match for \u201C${issue.input.slice(0, 120)}\u201D`, [...labels, cancelLabel]);
563
+ if (!selected || selected === cancelLabel) {
564
+ recordQlooResult(event.toolName, event.input, details, context);
565
+ return;
566
+ }
567
+ const selectedIndex = labels.indexOf(selected);
568
+ const selectedId = issue.candidates[selectedIndex]?.id;
569
+ if (selectedIndex < 0 || typeof selectedId !== "string") {
570
+ recordQlooResult(event.toolName, event.input, details, context);
571
+ return;
572
+ }
573
+ choices.push({ input: issue.input, selectedId });
574
+ }
575
+ const patchedInput = applyResolutionChoices(event.toolName, event.input, choices);
576
+ if (!patchedInput) {
577
+ recordQlooResult(event.toolName, event.input, details, context);
578
+ return;
579
+ }
580
+ try {
581
+ context.ui.setWorkingMessage("Qloo \xB7 applying your selection\u2026");
582
+ const retriedDetails = await commandActions.retry(event.toolName, patchedInput);
583
+ recordQlooResult(event.toolName, patchedInput, retriedDetails, context);
584
+ await maybeRunAutomaticDiagnosis(retriedDetails, context);
585
+ return {
586
+ content: [{ type: "text", text: JSON.stringify(retriedDetails) }],
587
+ details: retriedDetails,
588
+ isError: false
589
+ };
590
+ } catch {
591
+ context.ui.notify("Qloo could not rerun the workflow with that selection. The original ambiguity is preserved.", "error");
592
+ recordQlooResult(event.toolName, event.input, details, context);
593
+ return;
594
+ } finally {
595
+ context.ui.setWorkingMessage();
596
+ }
597
+ });
598
+ pi.on("tool_call", async (event, context) => {
599
+ const definition = activeDefinition();
600
+ if (!activeAllowedToolNames().has(event.toolName)) {
601
+ return {
602
+ block: true,
603
+ reason: `Tool "${event.toolName}" is unavailable in Qloo's ${activeProfile} profile.`
604
+ };
605
+ }
606
+ toolCallsThisTurn += 1;
607
+ if (toolCallsThisTurn > definition.maxToolCallsPerTurn) {
608
+ return {
609
+ block: true,
610
+ terminate: true,
611
+ reason: `Qloo stopped this turn after ${definition.maxToolCallsPerTurn} tool calls. Summarize progress or ask the user before continuing.`
612
+ };
613
+ }
614
+ const signature = stableToolSignature(event.toolName, event.input);
615
+ const identicalCount = (identicalCallsThisTurn.get(signature) ?? 0) + 1;
616
+ identicalCallsThisTurn.set(signature, identicalCount);
617
+ if (identicalCount > definition.maxIdenticalToolCallsPerTurn) {
618
+ return {
619
+ block: true,
620
+ terminate: true,
621
+ reason: `Qloo stopped a repeated ${event.toolName} call with unchanged input. Inspect the previous result or ask the user before retrying.`
622
+ };
623
+ }
624
+ if (!new Set(definition.approvalTools).has(event.toolName))
625
+ return void 0;
626
+ if (!context.hasUI) {
627
+ return {
628
+ block: true,
629
+ reason: `Tool "${event.toolName}" requires interactive approval in Qloo's ${activeProfile} profile.`
630
+ };
631
+ }
632
+ const approved = await context.ui.confirm(`Approve ${event.toolName}`, safeApprovalDetail(event.toolName, event.input));
633
+ return approved ? void 0 : {
634
+ block: true,
635
+ reason: `User denied ${event.toolName} in Qloo's ${activeProfile} profile.`
636
+ };
637
+ });
638
+ pi.on("user_bash", () => {
639
+ if (activeDefinition().allowDirectShell)
640
+ return void 0;
641
+ return {
642
+ result: {
643
+ output: `Direct shell execution is unavailable in Qloo's ${activeProfile} profile. Use qloo integrate or qloo build.
644
+ `,
645
+ exitCode: 1,
646
+ cancelled: false,
647
+ truncated: false
648
+ }
649
+ };
650
+ });
651
+ }
652
+ };
653
+ }
654
+ function createExplorePolicyExtension(allowedToolNames) {
655
+ return createHarnessPolicyExtension("explore", allowedToolNames);
656
+ }
657
+ export {
658
+ QLOO_SESSION_STATE_ENTRY_TYPE,
659
+ QLOO_SESSION_STATE_SCHEMA_VERSION,
660
+ QLOO_WORKSPACE_TOOL_NAMES,
661
+ buildExploreToolNames,
662
+ buildProfileToolNames,
663
+ createExplorePolicyExtension,
664
+ createHarnessPolicyExtension,
665
+ validateExploreTools
666
+ };