@sema-agent/client-core 0.110.0 → 0.111.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/CHANGELOG.md +169 -0
- package/README.md +50 -18
- package/dist/abortableSleep.d.ts +7 -0
- package/dist/abortableSleep.js +37 -0
- package/dist/adapt/arms.js +25 -4
- package/dist/adapt/toolCards.d.ts +3 -1
- package/dist/adapt/toolCards.js +4 -2
- package/dist/adapter/activeRunSelfHeal.d.ts +39 -0
- package/dist/adapter/activeRunSelfHeal.js +171 -3
- package/dist/adapter/downstream/eventToSdkMessage.js +10 -10
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -0
- package/dist/adapter/downstream/terminalToSdkResult.js +11 -0
- package/dist/adapter/runStream.d.ts +5 -0
- package/dist/adapter/runStream.js +15 -3
- package/dist/attachmentRoute.d.ts +20 -0
- package/dist/attachmentRoute.js +39 -0
- package/dist/attachmentsWireCaps.js +1 -1
- package/dist/builtinOutputStyles.d.ts +12 -0
- package/dist/builtinOutputStyles.js +147 -0
- package/dist/compactionStatus.d.ts +9 -0
- package/dist/compactionStatus.js +29 -5
- package/dist/controlRouter.js +2 -1
- package/dist/coreValuePorts.d.ts +39 -1
- package/dist/coreValuePorts.js +10 -0
- package/dist/displayUntrusted.d.ts +1 -0
- package/dist/displayUntrusted.js +6 -0
- package/dist/engineHealthFacts.d.ts +11 -0
- package/dist/engineHealthFacts.js +21 -0
- package/dist/engineHealthProbeVerdict.d.ts +23 -0
- package/dist/engineHealthProbeVerdict.js +115 -0
- package/dist/engineNoticeCodes.js +5 -7
- package/dist/engineRepoint.d.ts +12 -5
- package/dist/engineRepoint.js +44 -19
- package/dist/engineRowRef.d.ts +3 -0
- package/dist/engineRowRef.js +11 -1
- package/dist/engineSwapCrossing.d.ts +57 -0
- package/dist/engineSwapCrossing.js +297 -0
- package/dist/engineUnavailable.d.ts +44 -0
- package/dist/engineUnavailable.js +492 -0
- package/dist/eventLeg.js +3 -3
- package/dist/generated/sdkFactTables.d.ts +1 -1
- package/dist/generated/sdkFactTables.js +1 -0
- package/dist/generated/settingsSchemaTables.d.ts +4 -0
- package/dist/generated/settingsSchemaTables.js +1 -0
- package/dist/hitl/approvalDelivery.d.ts +1 -0
- package/dist/hitl/approvalDelivery.js +1 -1
- package/dist/hitl/approvalResolution.d.ts +1 -1
- package/dist/hitl/approvalResolution.js +2 -1
- package/dist/hitl/askGateWire.js +5 -6
- package/dist/hitl/decisionRefusal.d.ts +2 -0
- package/dist/hitl/decisionRefusal.js +7 -8
- package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
- package/dist/hitl/decisionRefusalCarrier.js +348 -0
- package/dist/hitl/decisionSend.d.ts +7 -0
- package/dist/hitl/decisionSend.js +34 -12
- package/dist/hitl/editedApprovalAcceptance.js +1 -1
- package/dist/hitl/elicitationRefusal.d.ts +4 -0
- package/dist/hitl/elicitationRefusal.js +23 -0
- package/dist/hitl/frameRouter.d.ts +2 -2
- package/dist/hitl/frameRouter.js +8 -15
- package/dist/hitl/gateIdentity.d.ts +5 -0
- package/dist/hitl/gateIdentity.js +23 -0
- package/dist/hitl/hitlBridge.d.ts +6 -0
- package/dist/hitl/hitlBridge.js +10 -4
- package/dist/hitl/hitlHostSurface.d.ts +2 -1
- package/dist/hitl/hitlHostSurface.js +7 -2
- package/dist/hitl/localAllowRedemption.d.ts +67 -0
- package/dist/hitl/localAllowRedemption.js +256 -0
- package/dist/hitl/parkResolver.js +7 -4
- package/dist/hitl/persistedRulesWire.d.ts +16 -0
- package/dist/hitl/persistedRulesWire.js +70 -0
- package/dist/hitl/planReviewWire.d.ts +3 -0
- package/dist/hitl/planReviewWire.js +23 -6
- package/dist/hitl/policyDenyCause.d.ts +1 -1
- package/dist/hitl/policyDenyCause.js +1 -1
- package/dist/hitl/resumeRunningCard.d.ts +1 -0
- package/dist/hitl/resumeRunningCard.js +1 -0
- package/dist/hitl/screenCardCarriers.d.ts +15 -0
- package/dist/hitl/screenCardCarriers.js +41 -0
- package/dist/hitl/screenDecisionCards.d.ts +98 -0
- package/dist/hitl/screenDecisionCards.js +362 -0
- package/dist/hitl/standingRows.d.ts +3 -1
- package/dist/hitl/standingRows.js +18 -0
- package/dist/hitl/toolApprovalWire.d.ts +30 -8
- package/dist/hitl/toolApprovalWire.js +264 -121
- package/dist/hooksWireCaps.js +3 -3
- package/dist/hostSettingNoticeArm.d.ts +3 -0
- package/dist/hostSettingNoticeArm.js +12 -0
- package/dist/hostTurnSettings.d.ts +3 -0
- package/dist/hostTurnSettings.js +38 -5
- package/dist/humanTurnRead.js +11 -1
- package/dist/i18n/messages.en.d.ts +129 -0
- package/dist/i18n/messages.en.js +135 -7
- package/dist/i18n/messages.zh.js +142 -6
- package/dist/i18n/translate.d.ts +3 -0
- package/dist/i18n/translate.js +78 -10
- package/dist/i18n/zh.d.ts +1 -0
- package/dist/i18n/zh.js +1 -0
- package/dist/index.d.ts +43 -17
- package/dist/index.js +30 -12
- package/dist/interactiveHalt.js +2 -1
- package/dist/interactiveInterrupt.js +20 -2
- package/dist/liveModelCatalog.js +2 -2
- package/dist/localEngineSeatReading.d.ts +2 -2
- package/dist/localEngineSeatReading.js +5 -3
- package/dist/localEngineSpawn.d.ts +0 -7
- package/dist/localEngineSpawn.js +0 -92
- package/dist/localEngineStep.d.ts +2 -2
- package/dist/localEngineStep.js +2 -0
- package/dist/mcpReconnect.js +3 -3
- package/dist/mcpWireCaps.js +1 -1
- package/dist/model/engineModelWords.d.ts +3 -0
- package/dist/model/engineModelWords.js +13 -0
- package/dist/model/subagentModelWord.d.ts +21 -2
- package/dist/model/subagentModelWord.js +100 -29
- package/dist/modelCapabilityProbe.js +3 -40
- package/dist/notifications.d.ts +19 -0
- package/dist/notifications.js +114 -12
- package/dist/ownKey.d.ts +6 -0
- package/dist/ownKey.js +31 -0
- package/dist/pendingUserRow.d.ts +4 -0
- package/dist/pendingUserRow.js +16 -0
- package/dist/permissionsDeclaration.d.ts +1 -0
- package/dist/permissionsDeclaration.js +11 -1
- package/dist/planReviewCardFacts.d.ts +3 -0
- package/dist/planReviewCardFacts.js +52 -2
- package/dist/pluginHooksWire.d.ts +9 -1
- package/dist/pluginHooksWire.js +40 -6
- package/dist/printToolResultFrame.js +4 -2
- package/dist/promptBlockedCopy.js +11 -6
- package/dist/promptSuggestionBatch.d.ts +7 -0
- package/dist/promptSuggestionBatch.js +9 -0
- package/dist/promptSuggestionsTail.d.ts +39 -0
- package/dist/promptSuggestionsTail.js +116 -0
- package/dist/request/taskRequest.js +10 -6
- package/dist/requestCoordinate.d.ts +1 -0
- package/dist/requestCoordinate.js +13 -0
- package/dist/rewindArchiveReading.js +4 -4
- package/dist/runCancelContext.d.ts +2 -1
- package/dist/runCancelContext.js +1 -0
- package/dist/runPermissionModeSwitch.js +2 -2
- package/dist/scenarioRefusal.js +11 -13
- package/dist/scenarioSkills.js +11 -6
- package/dist/seam.d.ts +16 -2
- package/dist/seam.js +10 -0
- package/dist/seatContract.d.ts +109 -9
- package/dist/seatContract.js +218 -15
- package/dist/sessionCostReading.d.ts +70 -0
- package/dist/sessionCostReading.js +150 -0
- package/dist/sessionHooksRoster.d.ts +4 -0
- package/dist/sessionHooksRoster.js +158 -0
- package/dist/sessionMemoryStatus.js +2 -2
- package/dist/sessionReadRedaction.d.ts +7 -0
- package/dist/sessionReadRedaction.js +26 -0
- package/dist/sessionSyncFileHistory.js +8 -9
- package/dist/settingsHooksSalvage.js +2 -1
- package/dist/sseIdleTriage.js +2 -1
- package/dist/storeDurabilityWord.d.ts +15 -0
- package/dist/storeDurabilityWord.js +18 -0
- package/dist/storePostureCapability.d.ts +7 -3
- package/dist/storePostureCapability.js +69 -39
- package/dist/subagent/engineSubagentResume.js +2 -2
- package/dist/subagent/engineSubagentTail.js +2 -1
- package/dist/subagentContentStore.d.ts +11 -0
- package/dist/subagentContentStore.js +28 -2
- package/dist/toolResult.d.ts +1 -0
- package/dist/toolResult.js +2 -0
- package/dist/userAttachmentEnvelope.d.ts +2 -1
- package/dist/userAttachmentEnvelope.js +10 -0
- package/dist/wireErrorTriage.js +2 -1
- package/dist/wireFailureShape.d.ts +4 -0
- package/dist/wireFailureShape.js +141 -19
- package/dist/wireRefusalCopy.d.ts +6 -5
- package/dist/wireRefusalCopy.js +49 -92
- package/docs/INTEGRATION-CLIENTS.md +2118 -17
- package/package.json +6 -2
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { UiLanguage } from './env/uiLanguage.js';
|
|
2
|
+
export declare const TOOLS_LOCATIONS: readonly ["this-machine", "elsewhere", "unconfirmed"];
|
|
3
|
+
export type ToolsLocation = (typeof TOOLS_LOCATIONS)[number];
|
|
4
|
+
export declare const ATTACHMENT_ROUTE_LEGS: readonly ["at_mention", "edited_file", "paste", "ide_selection"];
|
|
5
|
+
export type AttachmentRouteLeg = (typeof ATTACHMENT_ROUTE_LEGS)[number];
|
|
6
|
+
export interface AttachmentRouteFacts {
|
|
7
|
+
readonly leg: AttachmentRouteLeg;
|
|
8
|
+
readonly toolsLocation: ToolsLocation;
|
|
9
|
+
}
|
|
10
|
+
export type AttachmentWithheldReason = 'tools-elsewhere' | 'tools-location-unconfirmed';
|
|
11
|
+
export type AttachmentRoute = {
|
|
12
|
+
readonly route: 'attach_content';
|
|
13
|
+
readonly localCopy: boolean;
|
|
14
|
+
} | {
|
|
15
|
+
readonly route: 'withhold';
|
|
16
|
+
readonly reason: AttachmentWithheldReason;
|
|
17
|
+
};
|
|
18
|
+
export declare function attachmentRouteOf(facts: AttachmentRouteFacts): AttachmentRoute;
|
|
19
|
+
export declare function attachmentWithheldReasonText(reason: AttachmentWithheldReason, lang?: UiLanguage): string;
|
|
20
|
+
export declare function attachmentLocalCopyNote(): string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { msg, renderMessage } from './i18n/translate.js';
|
|
2
|
+
import { readOwnDataNonEmptyString } from './ownKey.js';
|
|
3
|
+
export const TOOLS_LOCATIONS = Object.freeze(['this-machine', 'elsewhere', 'unconfirmed']);
|
|
4
|
+
export const ATTACHMENT_ROUTE_LEGS = Object.freeze(['at_mention', 'edited_file', 'paste', 'ide_selection']);
|
|
5
|
+
const _withheldReasonsPinned = true;
|
|
6
|
+
void _withheldReasonsPinned;
|
|
7
|
+
const ATTACH = Object.freeze({ route: 'attach_content', localCopy: false });
|
|
8
|
+
const ATTACH_LOCAL_COPY = Object.freeze({ route: 'attach_content', localCopy: true });
|
|
9
|
+
const WITHHOLD_ELSEWHERE = Object.freeze({ route: 'withhold', reason: 'tools-elsewhere' });
|
|
10
|
+
const WITHHOLD_UNCONFIRMED = Object.freeze({ route: 'withhold', reason: 'tools-location-unconfirmed' });
|
|
11
|
+
const SNAPSHOT_LEGS = new Set(['paste', 'ide_selection']);
|
|
12
|
+
function closedWord(facts, key, words) {
|
|
13
|
+
try {
|
|
14
|
+
if (typeof facts !== 'object' || facts === null)
|
|
15
|
+
return undefined;
|
|
16
|
+
const raw = readOwnDataNonEmptyString(facts, key);
|
|
17
|
+
return raw !== undefined && words.includes(raw) ? raw : undefined;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function attachmentRouteOf(facts) {
|
|
24
|
+
const where = closedWord(facts, 'toolsLocation', TOOLS_LOCATIONS);
|
|
25
|
+
if (where === 'this-machine')
|
|
26
|
+
return ATTACH;
|
|
27
|
+
const leg = closedWord(facts, 'leg', ATTACHMENT_ROUTE_LEGS);
|
|
28
|
+
if (leg !== undefined && SNAPSHOT_LEGS.has(leg))
|
|
29
|
+
return ATTACH_LOCAL_COPY;
|
|
30
|
+
return where === 'elsewhere' ? WITHHOLD_ELSEWHERE : WITHHOLD_UNCONFIRMED;
|
|
31
|
+
}
|
|
32
|
+
export function attachmentWithheldReasonText(reason, lang) {
|
|
33
|
+
return reason === 'tools-elsewhere'
|
|
34
|
+
? renderMessage(msg('attachments.withheld.toolsElsewhere.row'), lang)
|
|
35
|
+
: renderMessage(msg('attachments.withheld.toolsLocationUnconfirmed.row'), lang);
|
|
36
|
+
}
|
|
37
|
+
export function attachmentLocalCopyNote() {
|
|
38
|
+
return renderMessage(msg('attachments.localCopy.model'));
|
|
39
|
+
}
|
|
@@ -25,7 +25,7 @@ const OPT_IN_KEYS = [
|
|
|
25
25
|
'mcpInstructions',
|
|
26
26
|
];
|
|
27
27
|
const DEFAULT_ON_ONLY_KEYS = ['agentListing', 'skillsListing'];
|
|
28
|
-
const PRODUCT_DEFAULT_KEYS = ['backgroundTasks', 'toolsDelta', 'todoReminder'];
|
|
28
|
+
const PRODUCT_DEFAULT_KEYS = ['backgroundTasks', 'toolsDelta', 'todoReminder', 'mcpInstructions'];
|
|
29
29
|
function productDefaultAttachments() {
|
|
30
30
|
const out = {};
|
|
31
31
|
for (const k of PRODUCT_DEFAULT_KEYS)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type BuiltinOutputStyleName = 'default' | 'Proactive' | 'Concise' | 'Explanatory' | 'Learning';
|
|
2
|
+
export interface BuiltinOutputStyle {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly source: 'built-in';
|
|
5
|
+
readonly description: string;
|
|
6
|
+
readonly keepCodingInstructions: true;
|
|
7
|
+
readonly prompt: string;
|
|
8
|
+
readonly turnReminder?: string;
|
|
9
|
+
readonly waitingTurnReminder?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const BUILTIN_OUTPUT_STYLES: Readonly<Record<BuiltinOutputStyleName, BuiltinOutputStyle | null>>;
|
|
12
|
+
export declare function builtinOutputStyleOf(name: string): BuiltinOutputStyle | null | undefined;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
const INSIGHTS_SECTION = `
|
|
2
|
+
## Insights
|
|
3
|
+
In order to encourage learning, before and after writing code, always provide brief educational explanations about implementation choices using (with backticks):
|
|
4
|
+
"\`★ Insight ─────────────────────────────────────\`
|
|
5
|
+
[2-3 key educational points]
|
|
6
|
+
\`─────────────────────────────────────────────────\`"
|
|
7
|
+
|
|
8
|
+
These insights should be included in the conversation, not in the codebase. You should generally focus on interesting insights that are specific to the codebase or the code you just wrote, rather than general programming concepts.`;
|
|
9
|
+
const PROACTIVE_SECTION = `The user chose continuous, autonomous execution. You should:
|
|
10
|
+
|
|
11
|
+
1. **Execute immediately** — Start implementing right away. Make reasonable assumptions and proceed on low-risk work.
|
|
12
|
+
2. **Minimize interruptions** — Prefer making reasonable assumptions over asking questions for routine decisions.
|
|
13
|
+
3. **Prefer action over planning** — Do not enter plan mode unless the user explicitly asks. When in doubt, start coding.
|
|
14
|
+
4. **Expect course corrections** — The user may provide suggestions or course corrections at any point; treat those as normal input.
|
|
15
|
+
5. **Do not take overly destructive actions** — This is not a license to destroy. Anything that deletes data or modifies shared or production systems still needs explicit user confirmation. If you reach such a decision point, ask and wait, or course correct to a safer method instead.
|
|
16
|
+
6. **Avoid data exfiltration** — Post even routine messages to chat platforms or work tickets only if the user has directed you to. You must not share secrets (e.g. credentials, internal documentation) unless the user has explicitly authorized both that specific secret and its destination.`;
|
|
17
|
+
const CONCISE_SECTION = `The user chose brevity over narration. You should:
|
|
18
|
+
|
|
19
|
+
1. **Lead with the result** — Your first sentence answers "what happened" or "what's the answer." No preamble ("Let me...", "Now I'll...") and no closing recap of what you already said.
|
|
20
|
+
2. **Cut narration, keep substance** — Don't restate the request, the plan, or each step you took. Report outcomes, decisions, and anything the user must act on.
|
|
21
|
+
3. **Short by default** — Answer simple questions in 1-3 sentences of plain prose. Use headers, tables, and bullet lists only when they carry real structure, never as decoration.
|
|
22
|
+
4. **State things plainly** — Skip hedging boilerplate. Mention a caveat only when it changes what the user should do next.
|
|
23
|
+
5. **Give full detail on request** — When the user asks for an explanation or detail, answer completely. Conciseness never means withholding requested information.
|
|
24
|
+
6. **Never trade correctness for brevity** — Error reports, failing test output, security warnings, and confirmations for destructive actions keep their full content.
|
|
25
|
+
|
|
26
|
+
Where these rules conflict with more general communication or formatting guidance elsewhere in your instructions, these rules win.`;
|
|
27
|
+
export const BUILTIN_OUTPUT_STYLES = Object.freeze({
|
|
28
|
+
default: null,
|
|
29
|
+
Proactive: Object.freeze({
|
|
30
|
+
name: 'Proactive',
|
|
31
|
+
source: 'built-in',
|
|
32
|
+
description: "Sema executes immediately, minimizes interruptions, and prefers action over planning",
|
|
33
|
+
keepCodingInstructions: true,
|
|
34
|
+
prompt: `You are an interactive CLI tool that helps users with software engineering tasks. You should work proactively and autonomously, executing immediately and minimizing interruptions.
|
|
35
|
+
|
|
36
|
+
# Proactive Style Active
|
|
37
|
+
${PROACTIVE_SECTION}`,
|
|
38
|
+
turnReminder: "Execute autonomously, minimize interruptions, prefer action over planning.",
|
|
39
|
+
waitingTurnReminder: "Execute autonomously and minimize interruptions. If the only work left is waiting for a background task or monitor you started, end your turn now: you will be notified when it finishes or fires. Do not poll, sleep, or re-read its output while you wait.",
|
|
40
|
+
}),
|
|
41
|
+
Concise: Object.freeze({
|
|
42
|
+
name: 'Concise',
|
|
43
|
+
source: 'built-in',
|
|
44
|
+
description: "Sema responds tersely, leading with results and skipping preamble and narration",
|
|
45
|
+
keepCodingInstructions: true,
|
|
46
|
+
prompt: `You are an interactive CLI tool that helps users with software engineering tasks. Keep your responses short and direct while doing the work just as thoroughly.
|
|
47
|
+
|
|
48
|
+
# Concise Style Active
|
|
49
|
+
${CONCISE_SECTION}`,
|
|
50
|
+
turnReminder: "Be concise: lead with the result, skip preamble and narration, keep only what the user needs.",
|
|
51
|
+
}),
|
|
52
|
+
Explanatory: Object.freeze({
|
|
53
|
+
name: 'Explanatory',
|
|
54
|
+
source: 'built-in',
|
|
55
|
+
description: "Sema explains its implementation choices and codebase patterns",
|
|
56
|
+
keepCodingInstructions: true,
|
|
57
|
+
prompt: `You are an interactive CLI tool that helps users with software engineering tasks. In addition to software engineering tasks, you should provide educational insights about the codebase along the way.
|
|
58
|
+
|
|
59
|
+
You should be clear and educational, providing helpful explanations while remaining focused on the task. Balance educational content with task completion. When providing insights, you may exceed typical length constraints, but remain focused and relevant.
|
|
60
|
+
|
|
61
|
+
# Explanatory Style Active
|
|
62
|
+
${INSIGHTS_SECTION}`,
|
|
63
|
+
}),
|
|
64
|
+
Learning: Object.freeze({
|
|
65
|
+
name: 'Learning',
|
|
66
|
+
source: 'built-in',
|
|
67
|
+
description: "Sema pauses and asks you to write small pieces of code for hands-on practice",
|
|
68
|
+
keepCodingInstructions: true,
|
|
69
|
+
prompt: `You are an interactive CLI tool that helps users with software engineering tasks. In addition to software engineering tasks, you should help users learn more about the codebase through hands-on practice and educational insights.
|
|
70
|
+
|
|
71
|
+
You should be collaborative and encouraging. Balance task completion with learning by requesting user input for meaningful design decisions while handling routine implementation yourself.
|
|
72
|
+
|
|
73
|
+
# Learning Style Active
|
|
74
|
+
## Requesting Human Contributions
|
|
75
|
+
In order to encourage learning, ask the human to contribute 2-10 line code pieces when generating 20+ lines involving:
|
|
76
|
+
- Design decisions (error handling, data structures)
|
|
77
|
+
- Business logic with multiple valid approaches
|
|
78
|
+
- Key algorithms or interface definitions
|
|
79
|
+
|
|
80
|
+
**TodoList Integration**: If using a TodoList for the overall task, include a specific todo item like "Request human input on [specific decision]" when planning to request human input. This ensures proper task tracking. Note: TodoList is not required for all tasks.
|
|
81
|
+
|
|
82
|
+
Example TodoList flow:
|
|
83
|
+
✓ "Set up component structure with placeholder for logic"
|
|
84
|
+
✓ "Request human collaboration on decision logic implementation"
|
|
85
|
+
✓ "Integrate contribution and complete feature"
|
|
86
|
+
|
|
87
|
+
### Request Format
|
|
88
|
+
\`\`\`
|
|
89
|
+
● **Learn by Doing**
|
|
90
|
+
**Context:** [what's built and why this decision matters]
|
|
91
|
+
**Your Task:** [specific function/section in file, mention file and TODO(human) but do not include line numbers]
|
|
92
|
+
**Guidance:** [trade-offs and constraints to consider]
|
|
93
|
+
\`\`\`
|
|
94
|
+
|
|
95
|
+
### Key Guidelines
|
|
96
|
+
- Frame contributions as valuable design decisions, not busy work
|
|
97
|
+
- You must first add a TODO(human) section into the codebase with your editing tools before making the Learn by Doing request
|
|
98
|
+
- Make sure there is one and only one TODO(human) section in the code
|
|
99
|
+
- Don't take any action or output anything after the Learn by Doing request. Wait for human implementation before proceeding.
|
|
100
|
+
|
|
101
|
+
### Example Requests
|
|
102
|
+
|
|
103
|
+
**Whole Function Example:**
|
|
104
|
+
\`\`\`
|
|
105
|
+
● **Learn by Doing**
|
|
106
|
+
|
|
107
|
+
**Context:** I've set up the hint feature UI with a button that triggers the hint system. The infrastructure is ready: when clicked, it calls selectHintCell() to determine which cell to hint, then highlights that cell with a yellow background and shows possible values. The hint system needs to decide which empty cell would be most helpful to reveal to the user.
|
|
108
|
+
|
|
109
|
+
**Your Task:** In sudoku.js, implement the selectHintCell(board) function. Look for TODO(human). This function should analyze the board and return {row, col} for the best cell to hint, or null if the puzzle is complete.
|
|
110
|
+
|
|
111
|
+
**Guidance:** Consider multiple strategies: prioritize cells with only one possible value (naked singles), or cells that appear in rows/columns/boxes with many filled cells. You could also consider a balanced approach that helps without making it too easy. The board parameter is a 9x9 array where 0 represents empty cells.
|
|
112
|
+
\`\`\`
|
|
113
|
+
|
|
114
|
+
**Partial Function Example:**
|
|
115
|
+
\`\`\`
|
|
116
|
+
● **Learn by Doing**
|
|
117
|
+
|
|
118
|
+
**Context:** I've built a file upload component that validates files before accepting them. The main validation logic is complete, but it needs specific handling for different file type categories in the switch statement.
|
|
119
|
+
|
|
120
|
+
**Your Task:** In upload.js, inside the validateFile() function's switch statement, implement the 'case "document":' branch. Look for TODO(human). This should validate document files (pdf, doc, docx).
|
|
121
|
+
|
|
122
|
+
**Guidance:** Consider checking file size limits (maybe 10MB for documents?), validating the file extension matches the MIME type, and returning {valid: boolean, error?: string}. The file object has properties: name, size, type.
|
|
123
|
+
\`\`\`
|
|
124
|
+
|
|
125
|
+
**Debugging Example:**
|
|
126
|
+
\`\`\`
|
|
127
|
+
● **Learn by Doing**
|
|
128
|
+
|
|
129
|
+
**Context:** The user reported that number inputs aren't working correctly in the calculator. I've identified the handleInput() function as the likely source, but need to understand what values are being processed.
|
|
130
|
+
|
|
131
|
+
**Your Task:** In calculator.js, inside the handleInput() function, add 2-3 console.log statements after the TODO(human) comment to help debug why number inputs fail.
|
|
132
|
+
|
|
133
|
+
**Guidance:** Consider logging: the raw input value, the parsed result, and any validation state. This will help us understand where the conversion breaks.
|
|
134
|
+
\`\`\`
|
|
135
|
+
|
|
136
|
+
### After Contributions
|
|
137
|
+
Share one insight connecting their code to broader patterns or system effects. Avoid praise or repetition.
|
|
138
|
+
|
|
139
|
+
## Insights
|
|
140
|
+
${INSIGHTS_SECTION}`,
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
export function builtinOutputStyleOf(name) {
|
|
144
|
+
if (typeof name !== 'string' || !Object.hasOwn(BUILTIN_OUTPUT_STYLES, name))
|
|
145
|
+
return undefined;
|
|
146
|
+
return BUILTIN_OUTPUT_STYLES[name];
|
|
147
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { UiLanguage } from './env/uiLanguage.js';
|
|
1
2
|
export interface CompactionStatusReading {
|
|
2
3
|
readonly compacting: boolean;
|
|
3
4
|
readonly since?: number;
|
|
5
|
+
readonly openTriggers?: readonly string[];
|
|
4
6
|
}
|
|
5
7
|
export type CompactionStatusChange = {
|
|
6
8
|
readonly kind: 'opened';
|
|
@@ -32,3 +34,10 @@ export interface PrintCompactionStatusCtx {
|
|
|
32
34
|
}
|
|
33
35
|
export declare function createCompactionStatusTracker(): CompactionStatusTracker;
|
|
34
36
|
export declare function compactionStatusFrameOf(change: CompactionStatusChange | null | undefined, ctx: PrintCompactionStatusCtx): PrintCompactionStatusFrame | null;
|
|
37
|
+
export type CompactionStopVerdict = {
|
|
38
|
+
readonly kind: 'compaction_canceled';
|
|
39
|
+
} | {
|
|
40
|
+
readonly kind: 'interrupted';
|
|
41
|
+
};
|
|
42
|
+
export declare function compactionStopVerdict(reading: CompactionStatusReading | null | undefined): CompactionStopVerdict;
|
|
43
|
+
export declare function compactionCanceledSentence(lang?: UiLanguage): string;
|
package/dist/compactionStatus.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { msg, renderMessage } from './i18n/translate.js';
|
|
2
|
+
import { appendOwnElement, readOwnDataValue } from './ownKey.js';
|
|
2
3
|
import { snapshotIsSubFlow, snapshotSegmentIdentity } from './adapter/types.js';
|
|
3
4
|
const _printStatusFramePins = [true, true];
|
|
4
5
|
void _printStatusFramePins;
|
|
@@ -20,9 +21,10 @@ function armLane(m) {
|
|
|
20
21
|
function openOf(pass, lane, startedAt, trigger) {
|
|
21
22
|
if (lane === 'unreadable')
|
|
22
23
|
return null;
|
|
23
|
-
|
|
24
|
+
const t = nonEmpty(trigger);
|
|
25
|
+
if (t === undefined)
|
|
24
26
|
return null;
|
|
25
|
-
return { kind: 'open', pass, main: lane === 'main', ...(typeof startedAt === 'number' && Number.isFinite(startedAt) ? { startedAt } : {}) };
|
|
27
|
+
return { kind: 'open', pass, main: lane === 'main', trigger: t, ...(typeof startedAt === 'number' && Number.isFinite(startedAt) ? { startedAt } : {}) };
|
|
26
28
|
}
|
|
27
29
|
function parse(input) {
|
|
28
30
|
const plane = own(input, 'plane');
|
|
@@ -101,7 +103,11 @@ export function createCompactionStatusTracker() {
|
|
|
101
103
|
if (mainOpen() === 0)
|
|
102
104
|
return { compacting: false };
|
|
103
105
|
const s = since();
|
|
104
|
-
|
|
106
|
+
const triggers = [];
|
|
107
|
+
for (const e of open.values())
|
|
108
|
+
if (e.main)
|
|
109
|
+
appendOwnElement(triggers, e.trigger);
|
|
110
|
+
return { compacting: true, ...(s !== undefined ? { since: s } : {}), openTriggers: Object.freeze(triggers) };
|
|
105
111
|
};
|
|
106
112
|
return {
|
|
107
113
|
take(input) {
|
|
@@ -115,7 +121,7 @@ export function createCompactionStatusTracker() {
|
|
|
115
121
|
if (open.has(p.pass) || closed.has(p.pass))
|
|
116
122
|
return null;
|
|
117
123
|
const before = mainOpen();
|
|
118
|
-
open.set(p.pass, { main: p.main === true, ...(p.startedAt !== undefined ? { startedAt: p.startedAt } : {}) });
|
|
124
|
+
open.set(p.pass, { main: p.main === true, trigger: p.trigger ?? '', ...(p.startedAt !== undefined ? { startedAt: p.startedAt } : {}) });
|
|
119
125
|
if (before === 0 && p.main === true) {
|
|
120
126
|
const s = since();
|
|
121
127
|
return { kind: 'opened', pass: p.pass, ...(s !== undefined ? { since: s } : {}) };
|
|
@@ -194,3 +200,21 @@ export function compactionStatusFrameOf(change, ctx) {
|
|
|
194
200
|
return null;
|
|
195
201
|
}
|
|
196
202
|
}
|
|
203
|
+
const STOP_CANCELED = Object.freeze({ kind: 'compaction_canceled' });
|
|
204
|
+
const STOP_INTERRUPTED = Object.freeze({ kind: 'interrupted' });
|
|
205
|
+
export function compactionStopVerdict(reading) {
|
|
206
|
+
try {
|
|
207
|
+
if (own(reading, 'compacting') !== true)
|
|
208
|
+
return STOP_INTERRUPTED;
|
|
209
|
+
const triggers = own(reading, 'openTriggers');
|
|
210
|
+
if (!Array.isArray(triggers) || own(triggers, 'length') !== 1)
|
|
211
|
+
return STOP_INTERRUPTED;
|
|
212
|
+
return own(triggers, '0') === 'manual' ? STOP_CANCELED : STOP_INTERRUPTED;
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
return STOP_INTERRUPTED;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
export function compactionCanceledSentence(lang) {
|
|
219
|
+
return renderMessage(msg('session.compaction.canceled'), lang);
|
|
220
|
+
}
|
package/dist/controlRouter.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cancelConflictContent, cancelConflictFromError } from './wireRefusalCopy.js';
|
|
2
|
+
import { STEERING_NOT_RUNNING } from './engineErrorCodes.js';
|
|
2
3
|
import { PROTOCOL_ERROR_NAME, THROWN_SLOT_UNREADABLE, protocolErrorNameOf, readThrownSlot, readThrownStatusSlot } from './wireFailureShape.js';
|
|
3
4
|
import { steerPriorityMode } from './steerPriorityCapability.js';
|
|
4
5
|
import { preemptReadingOfAck, preemptReadingOfError } from './preemptWire.js';
|
|
@@ -166,7 +167,7 @@ export function errCodes(e) {
|
|
|
166
167
|
return { code, name, status };
|
|
167
168
|
}
|
|
168
169
|
const STEERING_CODE_TO_SAFETY = new Map([
|
|
169
|
-
[
|
|
170
|
+
[STEERING_NOT_RUNNING, 'not_running'],
|
|
170
171
|
['steering.invalid_content', 'invalid_content'],
|
|
171
172
|
['steering.queue_full', 'queue_full'],
|
|
172
173
|
['steering.duplicate_input_id', 'duplicate_input_id'],
|
package/dist/coreValuePorts.d.ts
CHANGED
|
@@ -154,6 +154,40 @@ export interface CoreValuePorts {
|
|
|
154
154
|
runParkedOf(run: WorkflowRunParkedInputView): boolean;
|
|
155
155
|
parkedAgentsOf(agents: ReadonlyArray<WorkflowAgentStatusView>): WorkflowAgentStatusView[];
|
|
156
156
|
modelFaceResolvable(face: object, options?: ModelFaceOptionsView): ModelFaceVerdictView;
|
|
157
|
+
accountRowOfResult(result: {
|
|
158
|
+
readonly stats: AccountRowStatsView;
|
|
159
|
+
}): AccountRowView;
|
|
160
|
+
foldAccount(rows: Iterable<AccountRowView>): SessionAccountView;
|
|
161
|
+
}
|
|
162
|
+
export interface AccountRowView {
|
|
163
|
+
origin: 'run' | 'side_query';
|
|
164
|
+
tokens: number;
|
|
165
|
+
costMicroUsd?: number;
|
|
166
|
+
usageMissing?: true;
|
|
167
|
+
}
|
|
168
|
+
export interface AccountTotalView {
|
|
169
|
+
rows: number;
|
|
170
|
+
tokens: number;
|
|
171
|
+
costMicroUsd?: number;
|
|
172
|
+
usageMissing?: true;
|
|
173
|
+
}
|
|
174
|
+
export interface SessionAccountView extends AccountTotalView {
|
|
175
|
+
byOrigin: {
|
|
176
|
+
readonly run: AccountTotalView;
|
|
177
|
+
readonly side_query: AccountTotalView;
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
export interface AccountRowStatsView {
|
|
181
|
+
readonly tokens: number;
|
|
182
|
+
readonly costMicroUsd?: number;
|
|
183
|
+
readonly usageMissing?: true;
|
|
184
|
+
readonly nested?: AccountRowNestedStatsView;
|
|
185
|
+
}
|
|
186
|
+
export interface AccountRowNestedStatsView {
|
|
187
|
+
readonly tokens: number;
|
|
188
|
+
readonly turns: number;
|
|
189
|
+
readonly tasks: number;
|
|
190
|
+
readonly costMicroUsd?: number;
|
|
157
191
|
}
|
|
158
192
|
export interface WorkflowAgentStatusView {
|
|
159
193
|
readonly status: string;
|
|
@@ -176,7 +210,7 @@ export type ModelFaceVerdictView = {
|
|
|
176
210
|
readonly role: string;
|
|
177
211
|
readonly code: string;
|
|
178
212
|
};
|
|
179
|
-
export declare const CORE_VALUE_PORT_NAMES: readonly ["resolveAutonomousLoopPrompt", "loosenReasons", "combinePolicies", "createAllowDenyPolicy", "MCP_NAMESPACE", "protocolOf", "ruleToolGrammarOf", "validatePermissionRules", "mintNamespacePrefix", "compileAllowList", "routeKeyOf", "joinRequestUrl", "resolveRouteCredential", "adjudicateModelRoute", "routePairingStatus", "judgeRuleToolName", "readRuleEntry", "readMcpToolAxes", "ANTHROPIC_DEFAULT_BASE_URL", "readToolFaces", "ruleNameCovers", "IMPORTED_SETTINGS_LAYERS", "runParkedOf", "parkedAgentsOf", "modelFaceResolvable"];
|
|
213
|
+
export declare const CORE_VALUE_PORT_NAMES: readonly ["resolveAutonomousLoopPrompt", "loosenReasons", "combinePolicies", "createAllowDenyPolicy", "MCP_NAMESPACE", "protocolOf", "ruleToolGrammarOf", "validatePermissionRules", "mintNamespacePrefix", "compileAllowList", "routeKeyOf", "joinRequestUrl", "resolveRouteCredential", "adjudicateModelRoute", "routePairingStatus", "judgeRuleToolName", "readRuleEntry", "readMcpToolAxes", "ANTHROPIC_DEFAULT_BASE_URL", "readToolFaces", "ruleNameCovers", "IMPORTED_SETTINGS_LAYERS", "runParkedOf", "parkedAgentsOf", "modelFaceResolvable", "accountRowOfResult", "foldAccount"];
|
|
180
214
|
export type CoreValuePortName = (typeof CORE_VALUE_PORT_NAMES)[number];
|
|
181
215
|
export declare function installCoreValuePorts(ports: Partial<CoreValuePorts> | null): () => void;
|
|
182
216
|
export declare function hasCoreValuePorts(): boolean;
|
|
@@ -211,3 +245,7 @@ export declare function importedSettingsLayers(): CoreValuePorts['IMPORTED_SETTI
|
|
|
211
245
|
export declare function runParkedOf(run: WorkflowRunParkedInputView): boolean | undefined;
|
|
212
246
|
export declare function parkedAgentsOf(agents: ReadonlyArray<WorkflowAgentStatusView>): WorkflowAgentStatusView[] | undefined;
|
|
213
247
|
export declare function modelFaceResolvable(face: object, options?: ModelFaceOptionsView): ModelFaceVerdictView | undefined;
|
|
248
|
+
export declare function accountRowOfResult(result: {
|
|
249
|
+
readonly stats: AccountRowStatsView;
|
|
250
|
+
}): AccountRowView | undefined;
|
|
251
|
+
export declare function foldAccount(rows: Iterable<AccountRowView>): SessionAccountView | undefined;
|
package/dist/coreValuePorts.js
CHANGED
|
@@ -24,6 +24,8 @@ export const CORE_VALUE_PORT_NAMES = Object.freeze([
|
|
|
24
24
|
'runParkedOf',
|
|
25
25
|
'parkedAgentsOf',
|
|
26
26
|
'modelFaceResolvable',
|
|
27
|
+
'accountRowOfResult',
|
|
28
|
+
'foldAccount',
|
|
27
29
|
]);
|
|
28
30
|
let installed = null;
|
|
29
31
|
export function installCoreValuePorts(ports) {
|
|
@@ -142,3 +144,11 @@ export function modelFaceResolvable(face, options) {
|
|
|
142
144
|
const fn = port('modelFaceResolvable');
|
|
143
145
|
return fn === undefined ? undefined : fn(face, options);
|
|
144
146
|
}
|
|
147
|
+
export function accountRowOfResult(result) {
|
|
148
|
+
const fn = port('accountRowOfResult');
|
|
149
|
+
return fn === undefined ? undefined : fn(result);
|
|
150
|
+
}
|
|
151
|
+
export function foldAccount(rows) {
|
|
152
|
+
const fn = port('foldAccount');
|
|
153
|
+
return fn === undefined ? undefined : fn(rows);
|
|
154
|
+
}
|
|
@@ -51,6 +51,7 @@ export declare function capDisplay(text: string, max: number | undefined, mark:
|
|
|
51
51
|
export declare function paintAndCap(text: string, face: DisplayFace, max?: number): string;
|
|
52
52
|
export declare function credentialWashPasses(text: string, opts?: DisplayUntrustedOptions | null): Readonly<WashStats>;
|
|
53
53
|
export declare function displayUntrusted(text: string, opts?: DisplayUntrustedOptions): string;
|
|
54
|
+
export declare function displayUntrustedCapped(text: string, max: number, opts?: DisplayUntrustedOptions): string;
|
|
54
55
|
export declare function cleanUntrustedScalar(v: string | number | bigint | boolean | null | undefined): string;
|
|
55
56
|
export declare function queryCarriesCredential(query: string): boolean;
|
|
56
57
|
export declare function displayConfiguredUrl(url: string, opts?: DisplayUntrustedOptions): string;
|
package/dist/displayUntrusted.js
CHANGED
|
@@ -5236,6 +5236,12 @@ export function displayUntrusted(text, opts = {}) {
|
|
|
5236
5236
|
const out = renderUntrusted(text, opts, null);
|
|
5237
5237
|
return typeof out === 'string' ? out : out.tracked.text;
|
|
5238
5238
|
}
|
|
5239
|
+
export function displayUntrustedCapped(text, max, opts = {}) {
|
|
5240
|
+
const whole = displayUntrusted(text, opts);
|
|
5241
|
+
if (typeof max !== 'number' || !Number.isFinite(max) || whole.length <= max)
|
|
5242
|
+
return whole;
|
|
5243
|
+
return `${displayUntrusted(text, { ...opts, max })}…`;
|
|
5244
|
+
}
|
|
5239
5245
|
export function cleanUntrustedScalar(v) {
|
|
5240
5246
|
return displayUntrusted(String(v ?? ''), { credentialUrls: false, credentialWords: false, foldLines: false });
|
|
5241
5247
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { HealthAuthKind } from '@sema-agent/sdk';
|
|
2
|
+
import { type EngineHealthStoreDurability } from './storeDurabilityWord.js';
|
|
2
3
|
export interface EngineHealthCenterFacts {
|
|
3
4
|
readonly issuer?: string;
|
|
4
5
|
readonly webBaseUrl?: string;
|
|
@@ -51,4 +52,14 @@ export type ModsDeploymentPackagesReading = {
|
|
|
51
52
|
readonly kind: 'unreadable';
|
|
52
53
|
};
|
|
53
54
|
export declare function readModsDeploymentPackages(health: object | null | undefined): ModsDeploymentPackagesReading;
|
|
55
|
+
export type EngineHealthStoreReading = {
|
|
56
|
+
readonly kind: 'reported';
|
|
57
|
+
readonly durability: EngineHealthStoreDurability;
|
|
58
|
+
} | {
|
|
59
|
+
readonly kind: 'unreadable';
|
|
60
|
+
readonly word?: string;
|
|
61
|
+
} | {
|
|
62
|
+
readonly kind: 'absent';
|
|
63
|
+
};
|
|
64
|
+
export declare function readEngineHealthStore(health: object | null | undefined): EngineHealthStoreReading;
|
|
54
65
|
export declare function readEngineHealthFacts(health: object | null | undefined): EngineHealthFacts;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ownDataField, readOwnDataValue } from './ownKey.js';
|
|
2
|
+
import { readStoreDurability } from './storeDurabilityWord.js';
|
|
2
3
|
const _healthFactsMatchSdk = [true, true, true, true, true, true, true, true, true, true];
|
|
3
4
|
void _healthFactsMatchSdk;
|
|
4
5
|
const ENGINE_SIGNAL_KEY = 'engineSignal';
|
|
@@ -137,6 +138,26 @@ export function readModsDeploymentPackages(health) {
|
|
|
137
138
|
return Object.freeze({ kind: 'unreadable' });
|
|
138
139
|
}
|
|
139
140
|
}
|
|
141
|
+
export function readEngineHealthStore(health) {
|
|
142
|
+
try {
|
|
143
|
+
if (!isObject(health))
|
|
144
|
+
return Object.freeze({ kind: 'unreadable' });
|
|
145
|
+
const store = ownDataField(health, 'store');
|
|
146
|
+
if (store === undefined)
|
|
147
|
+
return Object.freeze({ kind: 'absent' });
|
|
148
|
+
if (!isObject(store))
|
|
149
|
+
return Object.freeze({ kind: 'unreadable' });
|
|
150
|
+
const read = readStoreDurability(store);
|
|
151
|
+
if (read.kind === 'word')
|
|
152
|
+
return Object.freeze({ kind: 'reported', durability: read.durability });
|
|
153
|
+
if (read.kind === 'unrecognized')
|
|
154
|
+
return Object.freeze({ kind: 'unreadable', word: read.word });
|
|
155
|
+
return Object.freeze({ kind: 'unreadable' });
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return Object.freeze({ kind: 'unreadable' });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
140
161
|
export function readEngineHealthFacts(health) {
|
|
141
162
|
if (!isObject(health))
|
|
142
163
|
return Object.freeze({});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { UiLanguage } from './env/uiLanguage.js';
|
|
2
|
+
import type { ProbeHealthResult } from '@sema-agent/sdk';
|
|
3
|
+
export type HealthProbeUnansweredReason = 'timeout' | 'network';
|
|
4
|
+
export type HealthProbeNotOkReason = 'http' | 'not_ok';
|
|
5
|
+
export interface HealthProbeAnsweredNotOk {
|
|
6
|
+
readonly kind: 'answered_not_ok';
|
|
7
|
+
readonly reason: HealthProbeNotOkReason;
|
|
8
|
+
readonly http: number;
|
|
9
|
+
readonly status?: string;
|
|
10
|
+
}
|
|
11
|
+
export type HealthProbeVerdict = {
|
|
12
|
+
readonly kind: 'unanswered';
|
|
13
|
+
readonly reason: HealthProbeUnansweredReason;
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: 'answered_ok';
|
|
16
|
+
} | HealthProbeAnsweredNotOk;
|
|
17
|
+
export interface TimedHealthAnswer {
|
|
18
|
+
readonly answer: HealthProbeAnsweredNotOk;
|
|
19
|
+
readonly atMs: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function classifyHealthProbe(reading: ProbeHealthResult): HealthProbeVerdict | undefined;
|
|
22
|
+
export declare function healthAnswerClause(verdict: HealthProbeVerdict, lang?: UiLanguage): string | undefined;
|
|
23
|
+
export declare function latestSegmentHealthAnswer(segmentStartMs: number | undefined, ...seen: ReadonlyArray<TimedHealthAnswer | undefined>): HealthProbeAnsweredNotOk | undefined;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { displayUntrusted } from './displayUntrusted.js';
|
|
2
|
+
import { msg, renderMessage } from './i18n/translate.js';
|
|
3
|
+
import { slotReadOf } from './wireFailureShape.js';
|
|
4
|
+
const UNANSWERED_REASONS = new Set(['timeout', 'network']);
|
|
5
|
+
const NOT_OK_REASONS = new Set(['http', 'not_ok']);
|
|
6
|
+
const REPORTED_STATUS_SHOWN_MAX = 64;
|
|
7
|
+
function ownValue(o, key) {
|
|
8
|
+
const r = slotReadOf(o, key);
|
|
9
|
+
return r.presence === 'present' ? r.value : undefined;
|
|
10
|
+
}
|
|
11
|
+
function isHttpCode(v) {
|
|
12
|
+
return typeof v === 'number' && Number.isInteger(v) && v >= 100 && v <= 599;
|
|
13
|
+
}
|
|
14
|
+
function shownStatusOf(v) {
|
|
15
|
+
if (typeof v !== 'string')
|
|
16
|
+
return undefined;
|
|
17
|
+
const shown = displayUntrusted(v, { max: REPORTED_STATUS_SHOWN_MAX });
|
|
18
|
+
return shown.trim() === '' ? undefined : shown;
|
|
19
|
+
}
|
|
20
|
+
function answeredNotOk(reason, http, status) {
|
|
21
|
+
return Object.freeze({ kind: 'answered_not_ok', reason, http, ...(status !== undefined ? { status } : {}) });
|
|
22
|
+
}
|
|
23
|
+
export function classifyHealthProbe(reading) {
|
|
24
|
+
try {
|
|
25
|
+
if (typeof reading !== 'object' || reading === null)
|
|
26
|
+
return undefined;
|
|
27
|
+
const alive = ownValue(reading, 'alive');
|
|
28
|
+
if (alive === true)
|
|
29
|
+
return Object.freeze({ kind: 'answered_ok' });
|
|
30
|
+
if (alive !== false)
|
|
31
|
+
return undefined;
|
|
32
|
+
const reason = ownValue(reading, 'reason');
|
|
33
|
+
if (typeof reason !== 'string')
|
|
34
|
+
return undefined;
|
|
35
|
+
if (UNANSWERED_REASONS.has(reason))
|
|
36
|
+
return Object.freeze({ kind: 'unanswered', reason: reason });
|
|
37
|
+
if (!NOT_OK_REASONS.has(reason))
|
|
38
|
+
return undefined;
|
|
39
|
+
const http = ownValue(reading, 'status');
|
|
40
|
+
if (!isHttpCode(http))
|
|
41
|
+
return undefined;
|
|
42
|
+
return answeredNotOk(reason, http, reason === 'not_ok' ? shownStatusOf(ownValue(reading, 'reportedStatus')) : undefined);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function answeredNotOkOf(v) {
|
|
49
|
+
if (typeof v !== 'object' || v === null)
|
|
50
|
+
return undefined;
|
|
51
|
+
if (ownValue(v, 'kind') !== 'answered_not_ok')
|
|
52
|
+
return undefined;
|
|
53
|
+
const reason = ownValue(v, 'reason');
|
|
54
|
+
const http = ownValue(v, 'http');
|
|
55
|
+
if (typeof reason !== 'string' || !NOT_OK_REASONS.has(reason) || !isHttpCode(http))
|
|
56
|
+
return undefined;
|
|
57
|
+
return answeredNotOk(reason, http, shownStatusOf(ownValue(v, 'status')));
|
|
58
|
+
}
|
|
59
|
+
export function healthAnswerClause(verdict, lang) {
|
|
60
|
+
try {
|
|
61
|
+
if (typeof verdict !== 'object' || verdict === null)
|
|
62
|
+
return undefined;
|
|
63
|
+
const kind = ownValue(verdict, 'kind');
|
|
64
|
+
if (kind === 'answered_ok')
|
|
65
|
+
return renderMessage(msg('localEngine.health.answer.ok'), lang);
|
|
66
|
+
if (kind === 'unanswered') {
|
|
67
|
+
const reason = ownValue(verdict, 'reason');
|
|
68
|
+
if (reason === 'timeout')
|
|
69
|
+
return renderMessage(msg('localEngine.health.answer.unansweredTimeout'), lang);
|
|
70
|
+
if (reason === 'network')
|
|
71
|
+
return renderMessage(msg('localEngine.health.answer.unansweredNetwork'), lang);
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
const nok = answeredNotOkOf(verdict);
|
|
75
|
+
if (nok === undefined)
|
|
76
|
+
return undefined;
|
|
77
|
+
return renderMessage(msg(nok.reason === 'not_ok' ? 'localEngine.health.answer.notOkStatus' : 'localEngine.health.answer.notOkHttp', { http: nok.http }), lang);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function timedNotOkOf(s, start, bestAtMs) {
|
|
84
|
+
try {
|
|
85
|
+
if (typeof s !== 'object' || s === null)
|
|
86
|
+
return { kind: 'not_better' };
|
|
87
|
+
const atMs = ownValue(s, 'atMs');
|
|
88
|
+
if (typeof atMs !== 'number' || !Number.isFinite(atMs))
|
|
89
|
+
return { kind: 'not_better' };
|
|
90
|
+
if (start !== undefined && atMs < start)
|
|
91
|
+
return { kind: 'not_better' };
|
|
92
|
+
if (bestAtMs !== undefined && atMs <= bestAtMs)
|
|
93
|
+
return { kind: 'not_better' };
|
|
94
|
+
const answer = answeredNotOkOf(ownValue(s, 'answer'));
|
|
95
|
+
return answer === undefined ? { kind: 'not_better' } : { kind: 'better', entry: { atMs, answer } };
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return { kind: 'unreadable' };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export function latestSegmentHealthAnswer(segmentStartMs, ...seen) {
|
|
102
|
+
try {
|
|
103
|
+
const start = typeof segmentStartMs === 'number' && Number.isFinite(segmentStartMs) ? segmentStartMs : undefined;
|
|
104
|
+
let best;
|
|
105
|
+
for (const s of seen) {
|
|
106
|
+
const r = timedNotOkOf(s, start, best?.atMs);
|
|
107
|
+
if (r.kind === 'better')
|
|
108
|
+
best = r.entry;
|
|
109
|
+
}
|
|
110
|
+
return best?.answer;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
}
|